@microsoft/1ds-post-js 3.2.12 → 4.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/bundle/es5/ms.post-4.0.1.gbl.js +5918 -0
- package/bundle/es5/ms.post-4.0.1.gbl.js.map +1 -0
- package/bundle/es5/ms.post-4.0.1.gbl.min.js +7 -0
- package/bundle/es5/ms.post-4.0.1.gbl.min.js.map +1 -0
- package/bundle/es5/ms.post-4.0.1.integrity.json +46 -0
- package/bundle/es5/ms.post-4.0.1.js +5922 -0
- package/bundle/es5/ms.post-4.0.1.js.map +1 -0
- package/bundle/es5/ms.post-4.0.1.min.js +7 -0
- package/bundle/es5/ms.post-4.0.1.min.js.map +1 -0
- package/bundle/es5/ms.post.gbl.js +5918 -0
- package/bundle/es5/ms.post.gbl.js.map +1 -0
- package/bundle/es5/ms.post.gbl.min.js +7 -0
- package/bundle/es5/ms.post.gbl.min.js.map +1 -0
- package/bundle/es5/ms.post.integrity.json +46 -0
- package/bundle/es5/ms.post.js +5922 -0
- package/bundle/es5/ms.post.js.map +1 -0
- package/bundle/es5/ms.post.min.js +7 -0
- package/bundle/es5/ms.post.min.js.map +1 -0
- package/{bundle → dist/es5}/ms.post.js +3026 -1652
- package/dist/es5/ms.post.js.map +1 -0
- package/dist/es5/ms.post.min.js +7 -0
- package/dist/es5/ms.post.min.js.map +1 -0
- package/{dist-esm/src → dist-es5}/BatchNotificationActions.js +1 -1
- package/{dist-esm/src → dist-es5}/ClockSkewManager.js +2 -2
- package/{dist-esm/src → dist-es5}/ClockSkewManager.js.map +1 -1
- package/{dist-esm/src → dist-es5}/DataModels.js +1 -1
- package/{dist-esm/src → dist-es5}/EventBatch.js +1 -1
- package/{dist-esm/src → dist-es5}/HttpManager.js +175 -94
- package/dist-es5/HttpManager.js.map +1 -0
- package/{dist-esm/src → dist-es5}/Index.js +2 -2
- package/dist-es5/Index.js.map +1 -0
- package/{dist-esm/src → dist-es5}/InternalConstants.js +1 -1
- package/{dist-esm/src → dist-es5}/KillSwitch.js +2 -2
- package/{dist-esm/src → dist-es5}/KillSwitch.js.map +1 -1
- package/{dist-esm/src → dist-es5}/PostChannel.js +166 -144
- package/dist-es5/PostChannel.js.map +1 -0
- package/{dist-esm/src → dist-es5}/RetryPolicy.js +1 -1
- package/{dist-esm/src → dist-es5}/Serializer.js +3 -2
- package/dist-es5/Serializer.js.map +1 -0
- package/dist-es5/TimeoutOverrideWrapper.js +24 -0
- package/dist-es5/TimeoutOverrideWrapper.js.map +1 -0
- package/{dist-esm/src → dist-es5}/typings/XDomainRequest.js +1 -1
- package/package.json +15 -10
- package/tsconfig.json +5 -2
- package/{src/DataModels.ts → types/1ds-post-js.d.ts} +408 -467
- package/types/1ds-post-js.namespaced.d.ts +404 -0
- package/bundle/ms.post-3.2.12.gbl.js +0 -4524
- package/bundle/ms.post-3.2.12.gbl.js.map +0 -1
- package/bundle/ms.post-3.2.12.gbl.min.js +0 -7
- package/bundle/ms.post-3.2.12.gbl.min.js.map +0 -1
- package/bundle/ms.post-3.2.12.integrity.json +0 -46
- package/bundle/ms.post-3.2.12.js +0 -4527
- package/bundle/ms.post-3.2.12.js.map +0 -1
- package/bundle/ms.post-3.2.12.min.js +0 -7
- package/bundle/ms.post-3.2.12.min.js.map +0 -1
- package/bundle/ms.post.gbl.js +0 -4524
- package/bundle/ms.post.gbl.js.map +0 -1
- package/bundle/ms.post.gbl.min.js +0 -7
- package/bundle/ms.post.gbl.min.js.map +0 -1
- package/bundle/ms.post.integrity.json +0 -46
- package/bundle/ms.post.js.map +0 -1
- package/bundle/ms.post.min.js +0 -7
- package/bundle/ms.post.min.js.map +0 -1
- package/dist/ms.post.js +0 -2144
- package/dist/ms.post.js.map +0 -1
- package/dist/ms.post.min.js +0 -7
- package/dist/ms.post.min.js.map +0 -1
- package/dist-esm/src/BatchNotificationActions.d.ts +0 -36
- package/dist-esm/src/ClockSkewManager.d.ts +0 -38
- package/dist-esm/src/DataModels.d.ts +0 -405
- package/dist-esm/src/EventBatch.d.ts +0 -47
- package/dist-esm/src/HttpManager.d.ts +0 -88
- package/dist-esm/src/HttpManager.js.map +0 -1
- package/dist-esm/src/Index.d.ts +0 -9
- package/dist-esm/src/Index.js.map +0 -1
- package/dist-esm/src/InternalConstants.d.ts +0 -28
- package/dist-esm/src/KillSwitch.d.ts +0 -26
- package/dist-esm/src/PostChannel.d.ts +0 -101
- package/dist-esm/src/PostChannel.js.map +0 -1
- package/dist-esm/src/RetryPolicy.d.ts +0 -21
- package/dist-esm/src/Serializer.d.ts +0 -108
- package/dist-esm/src/Serializer.js.map +0 -1
- package/dist-esm/src/TimeoutOverrideWrapper.d.ts +0 -18
- package/dist-esm/src/TimeoutOverrideWrapper.js +0 -28
- package/dist-esm/src/TimeoutOverrideWrapper.js.map +0 -1
- package/dist-esm/src/typings/XDomainRequest.d.ts +0 -17
- package/src/BatchNotificationActions.ts +0 -44
- package/src/ClockSkewManager.ts +0 -127
- package/src/EventBatch.ts +0 -137
- package/src/HttpManager.ts +0 -1379
- package/src/Index.ts +0 -18
- package/src/InternalConstants.ts +0 -42
- package/src/KillSwitch.ts +0 -84
- package/src/PostChannel.ts +0 -1163
- package/src/RetryPolicy.ts +0 -46
- package/src/Serializer.ts +0 -487
- package/src/TimeoutOverrideWrapper.ts +0 -29
- package/src/typings/XDomainRequest.ts +0 -23
- /package/{dist-esm/src → dist-es5}/BatchNotificationActions.js.map +0 -0
- /package/{dist-esm/src → dist-es5}/DataModels.js.map +0 -0
- /package/{dist-esm/src → dist-es5}/EventBatch.js.map +0 -0
- /package/{dist-esm/src → dist-es5}/InternalConstants.js.map +0 -0
- /package/{dist-esm/src → dist-es5}/RetryPolicy.js.map +0 -0
- /package/{dist-esm/src → dist-es5}/typings/XDomainRequest.js.map +0 -0
package/src/HttpManager.ts
DELETED
|
@@ -1,1379 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* HttpManager.ts
|
|
3
|
-
* @author Abhilash Panwar (abpanwar); Hector Hernandez (hectorh); Nev Wylie (newylie)
|
|
4
|
-
* @copyright Microsoft 2018-2020
|
|
5
|
-
*/
|
|
6
|
-
import dynamicProto from "@microsoft/dynamicproto-js";
|
|
7
|
-
import EVTClockSkewManager from "./ClockSkewManager";
|
|
8
|
-
import EVTKillSwitch from "./KillSwitch";
|
|
9
|
-
import {
|
|
10
|
-
EventSendType, FullVersionString, ICookieMgr, IDiagnosticLogger, IExtendedAppInsightsCore, IExtendedConfiguration, IPerfEvent,
|
|
11
|
-
SendRequestReason, TransportType, _eExtendedInternalMessageId, _eInternalMessageId, _throwInternal, _warnToConsole, arrForEach, dateNow,
|
|
12
|
-
doPerf, dumpObj, eLoggingSeverity, extend, getLocation, getNavigator, getTime, hasOwnProperty, isArray, isBeaconsSupported,
|
|
13
|
-
isFetchSupported, isNullOrUndefined, isNumber, isReactNative, isString, isUndefined, isValueAssigned, isXhrSupported, objForEachKey,
|
|
14
|
-
objKeys, openXhr, strTrim, strUndefined, useXDomainRequest
|
|
15
|
-
} from "@microsoft/1ds-core-js";
|
|
16
|
-
import { BatchNotificationAction, BatchNotificationActions } from "./BatchNotificationActions";
|
|
17
|
-
import {
|
|
18
|
-
EventBatchNotificationReason, ICollectorResult, IPayloadData, IPostChannel, IPostTransmissionTelemetryItem, IXHROverride,
|
|
19
|
-
PayloadListenerFunction, PayloadPreprocessorFunction, SendPOSTFunction
|
|
20
|
-
} from "./DataModels";
|
|
21
|
-
import { EventBatch } from "./EventBatch";
|
|
22
|
-
import { IChannelConfiguration } from "./Index";
|
|
23
|
-
import {
|
|
24
|
-
DEFAULT_CACHE_CONTROL, DEFAULT_CONTENT_TYPE, STR_API_KEY, STR_AUTH_XTOKEN, STR_CACHE_CONTROL, STR_CLIENT_ID, STR_CLIENT_VERSION,
|
|
25
|
-
STR_CONTENT_TYPE_HEADER, STR_DISABLED_PROPERTY_NAME, STR_DROPPED, STR_EMPTY, STR_KILL_DURATION_HEADER, STR_KILL_DURATION_SECONDS_HEADER,
|
|
26
|
-
STR_KILL_TOKENS_HEADER, STR_MSA_DEVICE_TICKET, STR_MSFPC, STR_NO_RESPONSE_BODY, STR_OTHER, STR_POST_METHOD, STR_REQUEUE,
|
|
27
|
-
STR_RESPONSE_FAIL, STR_SENDING, STR_TIME_DELTA_HEADER, STR_TIME_DELTA_TO_APPLY, STR_UPLOAD_TIME
|
|
28
|
-
} from "./InternalConstants";
|
|
29
|
-
import { retryPolicyGetMillisToBackoffForRetry, retryPolicyShouldRetryForStatus } from "./RetryPolicy";
|
|
30
|
-
import { ISerializedPayload, Serializer } from "./Serializer";
|
|
31
|
-
import { ITimeoutOverrideWrapper } from "./TimeoutOverrideWrapper";
|
|
32
|
-
import { XDomainRequest as IXDomainRequest } from "./typings/XDomainRequest";
|
|
33
|
-
|
|
34
|
-
const strSendAttempt = "sendAttempt";
|
|
35
|
-
|
|
36
|
-
const _noResponseQs = "&" + STR_NO_RESPONSE_BODY + "=true";
|
|
37
|
-
|
|
38
|
-
// TypeScript removed this interface so we need to declare the global so we can check for it's existence.
|
|
39
|
-
declare var XDomainRequest: {
|
|
40
|
-
prototype: IXDomainRequest;
|
|
41
|
-
new(): IXDomainRequest;
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
interface IRequestUrlDetails {
|
|
45
|
-
url: string,
|
|
46
|
-
hdrs: { [key: string]: string },
|
|
47
|
-
useHdrs: boolean
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Identifies the default notification reason to the action names
|
|
52
|
-
*/
|
|
53
|
-
const _eventActionMap: any = {
|
|
54
|
-
[EventBatchNotificationReason.Paused]: STR_REQUEUE,
|
|
55
|
-
[EventBatchNotificationReason.RequeueEvents]: STR_REQUEUE,
|
|
56
|
-
[EventBatchNotificationReason.Complete]: "sent",
|
|
57
|
-
[EventBatchNotificationReason.KillSwitch]: STR_DROPPED,
|
|
58
|
-
[EventBatchNotificationReason.SizeLimitExceeded]: STR_DROPPED
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
const _collectorQsHeaders = { };
|
|
62
|
-
const _collectorHeaderToQs = { };
|
|
63
|
-
|
|
64
|
-
function _addCollectorHeaderQsMapping(qsName: string, headerName: string, allowQs?: boolean) {
|
|
65
|
-
_collectorQsHeaders[qsName] = headerName;
|
|
66
|
-
if (allowQs !== false) {
|
|
67
|
-
_collectorHeaderToQs[headerName] = qsName;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
_addCollectorHeaderQsMapping(STR_MSA_DEVICE_TICKET, STR_MSA_DEVICE_TICKET, false);
|
|
72
|
-
_addCollectorHeaderQsMapping(STR_CLIENT_VERSION, STR_CLIENT_VERSION);
|
|
73
|
-
_addCollectorHeaderQsMapping(STR_CLIENT_ID, "Client-Id");
|
|
74
|
-
_addCollectorHeaderQsMapping(STR_API_KEY, STR_API_KEY);
|
|
75
|
-
_addCollectorHeaderQsMapping(STR_TIME_DELTA_TO_APPLY, STR_TIME_DELTA_TO_APPLY);
|
|
76
|
-
_addCollectorHeaderQsMapping(STR_UPLOAD_TIME, STR_UPLOAD_TIME);
|
|
77
|
-
_addCollectorHeaderQsMapping(STR_AUTH_XTOKEN, STR_AUTH_XTOKEN);
|
|
78
|
-
|
|
79
|
-
type OnCompleteCallback = (status: number, headers: { [headerName: string]: string }, response?: string) => void;
|
|
80
|
-
|
|
81
|
-
function _getResponseText(xhr: XMLHttpRequest | IXDomainRequest) {
|
|
82
|
-
try {
|
|
83
|
-
return xhr.responseText;
|
|
84
|
-
} catch (e) {
|
|
85
|
-
// Best effort, as XHR may throw while XDR wont so just ignore
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
return STR_EMPTY;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
interface IInternalXhrOverride extends IXHROverride {
|
|
92
|
-
_transport?: TransportType;
|
|
93
|
-
_isSync?: boolean;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
interface IInternalPayloadData extends IPayloadData {
|
|
97
|
-
_thePayload: ISerializedPayload;
|
|
98
|
-
_sendReason: SendRequestReason;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
function _hasHeader(headers: any, header: string) {
|
|
102
|
-
let hasHeader = false;
|
|
103
|
-
if (headers && header) {
|
|
104
|
-
const keys = objKeys(headers);
|
|
105
|
-
if (keys && keys.length > 0) {
|
|
106
|
-
const lowerHeader = header.toLowerCase();
|
|
107
|
-
for (let lp = 0; lp < keys.length; lp++) {
|
|
108
|
-
const value = keys[lp];
|
|
109
|
-
if (value && hasOwnProperty(header, value) &&
|
|
110
|
-
value.toLowerCase() === lowerHeader) {
|
|
111
|
-
hasHeader = true;
|
|
112
|
-
break;
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
return hasHeader;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
function _addRequestDetails(details: IRequestUrlDetails, name: string, value: string, useHeaders: boolean) {
|
|
122
|
-
if (name && value && value.length > 0) {
|
|
123
|
-
if (useHeaders && _collectorQsHeaders[name]) {
|
|
124
|
-
details.hdrs[_collectorQsHeaders[name]] = value;
|
|
125
|
-
details.useHdrs = true;
|
|
126
|
-
} else {
|
|
127
|
-
details.url += "&" + name + "=" + value;
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
function _prependTransports(theTransports: TransportType[], newTransports: TransportType | TransportType[]) {
|
|
133
|
-
if (newTransports) {
|
|
134
|
-
if (isNumber(newTransports)) {
|
|
135
|
-
theTransports = [newTransports as TransportType].concat(theTransports);
|
|
136
|
-
} else if (isArray(newTransports)) {
|
|
137
|
-
theTransports = newTransports.concat(theTransports);
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
return theTransports;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* Class managing the sending of requests.
|
|
147
|
-
*/
|
|
148
|
-
export class HttpManager {
|
|
149
|
-
public sendHook: PayloadPreprocessorFunction | undefined;
|
|
150
|
-
public sendListener: PayloadListenerFunction | undefined;
|
|
151
|
-
public _responseHandlers: Array<(responseText: string) => void> = [];
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
* @constructor
|
|
155
|
-
* @param requestQueue - The queue that contains the requests to be sent.
|
|
156
|
-
*/
|
|
157
|
-
constructor(maxEventsPerBatch: number, maxConnections: number, maxRequestRetriesBeforeBackoff: number, actions: BatchNotificationActions, timeoutOverride: ITimeoutOverrideWrapper) {
|
|
158
|
-
let _urlString: string = "?cors=true&" + STR_CONTENT_TYPE_HEADER.toLowerCase() + "=" + DEFAULT_CONTENT_TYPE;
|
|
159
|
-
let _killSwitch: EVTKillSwitch = new EVTKillSwitch();
|
|
160
|
-
let _paused = false;
|
|
161
|
-
let _clockSkewManager = new EVTClockSkewManager();
|
|
162
|
-
let _useBeacons = false;
|
|
163
|
-
let _outstandingRequests = 0; // Holds the number of outstanding async requests that have not returned a response yet
|
|
164
|
-
let _postManager: IPostChannel;
|
|
165
|
-
let _logger: IDiagnosticLogger;
|
|
166
|
-
let _sendInterfaces: { [key: number]: IInternalXhrOverride };
|
|
167
|
-
let _core: IExtendedAppInsightsCore;
|
|
168
|
-
let _customHttpInterface = true;
|
|
169
|
-
let _queryStringParameters: Array<{ name: string, value: string }> = [];
|
|
170
|
-
let _headers: { [name: string]: string } = {};
|
|
171
|
-
let _batchQueue: EventBatch[] = [];
|
|
172
|
-
let _serializer: Serializer = null;
|
|
173
|
-
let _enableEventTimings = false;
|
|
174
|
-
let _cookieMgr: ICookieMgr;
|
|
175
|
-
let _isUnloading = false;
|
|
176
|
-
let _useHeaders = false;
|
|
177
|
-
let _xhrTimeout: number;
|
|
178
|
-
let _disableXhrSync: boolean;
|
|
179
|
-
let _disableFetchKeepAlive: boolean;
|
|
180
|
-
let _canHaveReducedPayload: boolean;
|
|
181
|
-
let _addNoResponse: boolean;
|
|
182
|
-
|
|
183
|
-
dynamicProto(HttpManager, this, (_self) => {
|
|
184
|
-
let _sendCredentials = true;
|
|
185
|
-
|
|
186
|
-
_self.initialize = (
|
|
187
|
-
endpointUrl: string,
|
|
188
|
-
core: IExtendedAppInsightsCore,
|
|
189
|
-
postChannel: IPostChannel,
|
|
190
|
-
httpInterface: IXHROverride,
|
|
191
|
-
channelConfig?: IChannelConfiguration) => {
|
|
192
|
-
|
|
193
|
-
if (!channelConfig) {
|
|
194
|
-
channelConfig = {};
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
_urlString = endpointUrl + _urlString;
|
|
198
|
-
|
|
199
|
-
_useHeaders = !isUndefined(channelConfig.avoidOptions) ? !channelConfig.avoidOptions : true;
|
|
200
|
-
_core = core;
|
|
201
|
-
_cookieMgr = core.getCookieMgr();
|
|
202
|
-
_enableEventTimings = !(_core.config as IExtendedConfiguration).disableEventTimings;
|
|
203
|
-
let enableCompoundKey = !!(_core.config as IExtendedConfiguration).enableCompoundKey;
|
|
204
|
-
_postManager = postChannel;
|
|
205
|
-
_logger = _postManager.diagLog();
|
|
206
|
-
let valueSanitizer = channelConfig.valueSanitizer;
|
|
207
|
-
let stringifyObjects = channelConfig.stringifyObjects;
|
|
208
|
-
if (!isUndefined(channelConfig.enableCompoundKey)) {
|
|
209
|
-
enableCompoundKey = !!channelConfig.enableCompoundKey;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
_xhrTimeout = channelConfig.xhrTimeout;
|
|
213
|
-
_disableXhrSync = !!channelConfig.disableXhrSync;
|
|
214
|
-
_disableFetchKeepAlive = !!channelConfig.disableFetchKeepAlive;
|
|
215
|
-
_addNoResponse = channelConfig.addNoResponse !== false;
|
|
216
|
-
|
|
217
|
-
_useBeacons = !isReactNative(); // Only use beacons if not running in React Native
|
|
218
|
-
_serializer = new Serializer(_core, valueSanitizer, stringifyObjects, enableCompoundKey);
|
|
219
|
-
|
|
220
|
-
if (!isNullOrUndefined(channelConfig.useSendBeacon)) {
|
|
221
|
-
_useBeacons = !!channelConfig.useSendBeacon;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
let syncHttpInterface = httpInterface;
|
|
225
|
-
let beaconHttpInterface: IXHROverride = channelConfig.alwaysUseXhrOverride ? httpInterface : null;
|
|
226
|
-
let fetchSyncHttpInterface: IXHROverride = channelConfig.alwaysUseXhrOverride ? httpInterface : null;
|
|
227
|
-
let beaconUnloadTransports: TransportType[] = [TransportType.Beacon, TransportType.Fetch];
|
|
228
|
-
|
|
229
|
-
if (!httpInterface) {
|
|
230
|
-
_customHttpInterface = false;
|
|
231
|
-
|
|
232
|
-
let location = getLocation();
|
|
233
|
-
if (location && location.protocol && location.protocol.toLowerCase() === "file:") {
|
|
234
|
-
// Special case where a local html file fails with a CORS error on Chromium browsers
|
|
235
|
-
_sendCredentials = false;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
let theTransports: TransportType[] = [];
|
|
239
|
-
if (isReactNative()) {
|
|
240
|
-
// Use Fetch or XDR/XHR
|
|
241
|
-
theTransports = [TransportType.Fetch, TransportType.Xhr];
|
|
242
|
-
beaconUnloadTransports = [TransportType.Fetch, TransportType.Xhr, TransportType.Beacon];
|
|
243
|
-
} else {
|
|
244
|
-
// Use XDR/XHR, Fetch or beacons
|
|
245
|
-
theTransports = [TransportType.Xhr, TransportType.Fetch, TransportType.Beacon];
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
// Prefix any user requested transport(s) values
|
|
249
|
-
theTransports = _prependTransports(theTransports, channelConfig.transports);
|
|
250
|
-
|
|
251
|
-
httpInterface = _getSenderInterface(theTransports, false);
|
|
252
|
-
if (!httpInterface) {
|
|
253
|
-
_warnToConsole(_logger, "No available transport to send events");
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
syncHttpInterface = _getSenderInterface(theTransports, true);
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
if (!beaconHttpInterface) {
|
|
260
|
-
// Allow overriding the usage of sendBeacon
|
|
261
|
-
beaconUnloadTransports = _prependTransports(beaconUnloadTransports, channelConfig.unloadTransports);
|
|
262
|
-
beaconHttpInterface = _getSenderInterface(beaconUnloadTransports, true);
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
_canHaveReducedPayload = !_customHttpInterface && ((_useBeacons && isBeaconsSupported()) || (!_disableFetchKeepAlive && isFetchSupported(true)));
|
|
266
|
-
|
|
267
|
-
_sendInterfaces = {
|
|
268
|
-
[EventSendType.Batched]: httpInterface,
|
|
269
|
-
[EventSendType.Synchronous]: syncHttpInterface || _getSenderInterface([TransportType.Xhr, TransportType.Fetch, TransportType.Beacon], true),
|
|
270
|
-
[EventSendType.SendBeacon]: beaconHttpInterface || syncHttpInterface || _getSenderInterface([TransportType.Xhr], true),
|
|
271
|
-
[EventSendType.SyncFetch]: fetchSyncHttpInterface || _getSenderInterface([TransportType.Fetch, TransportType.Beacon], true) || syncHttpInterface || _getSenderInterface([TransportType.Xhr], true)
|
|
272
|
-
};
|
|
273
|
-
};
|
|
274
|
-
|
|
275
|
-
// Special internal method to allow the DebugPlugin to hook embedded objects
|
|
276
|
-
function _getSenderInterface(transports: TransportType[], syncSupport: boolean): IInternalXhrOverride {
|
|
277
|
-
let transportType: TransportType = TransportType.NotSet;
|
|
278
|
-
let sendPostFunc: SendPOSTFunction = null;
|
|
279
|
-
let lp = 0;
|
|
280
|
-
while (sendPostFunc == null && lp < transports.length) {
|
|
281
|
-
transportType = transports[lp];
|
|
282
|
-
if (transportType === TransportType.Xhr) {
|
|
283
|
-
if (useXDomainRequest()) {
|
|
284
|
-
sendPostFunc = _xdrSendPost;
|
|
285
|
-
} else if (isXhrSupported()) {
|
|
286
|
-
sendPostFunc = _xhrSendPost;
|
|
287
|
-
}
|
|
288
|
-
} else if (transportType === TransportType.Fetch && isFetchSupported(syncSupport) && (!syncSupport || (syncSupport && !_disableFetchKeepAlive))) {
|
|
289
|
-
sendPostFunc = _fetchSendPost;
|
|
290
|
-
} else if (_useBeacons && transportType === TransportType.Beacon && isBeaconsSupported()) {
|
|
291
|
-
sendPostFunc = _beaconSendPost;
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
lp++;
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
if (sendPostFunc) {
|
|
298
|
-
return {
|
|
299
|
-
_transport: transportType,
|
|
300
|
-
_isSync: syncSupport,
|
|
301
|
-
sendPOST: sendPostFunc
|
|
302
|
-
};
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
return null;
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
_self["_getDbgPlgTargets"] = () => {
|
|
309
|
-
return [_sendInterfaces[EventSendType.Batched], _killSwitch, _serializer, _sendInterfaces];
|
|
310
|
-
};
|
|
311
|
-
|
|
312
|
-
function _xdrSendPost(payload: IPayloadData, oncomplete: OnCompleteCallback, sync?: boolean) {
|
|
313
|
-
// It doesn't support custom headers, so no action is taken with current requestHeaders
|
|
314
|
-
let xdr = new XDomainRequest();
|
|
315
|
-
xdr.open(STR_POST_METHOD, payload.urlString);
|
|
316
|
-
if (payload.timeout) {
|
|
317
|
-
xdr.timeout = payload.timeout;
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
// can't get the status code in xdr.
|
|
321
|
-
xdr.onload = () => {
|
|
322
|
-
// we will assume onload means the request succeeded.
|
|
323
|
-
let response = _getResponseText(xdr);
|
|
324
|
-
_doOnComplete(oncomplete, 200, {}, response);
|
|
325
|
-
_handleCollectorResponse(response);
|
|
326
|
-
};
|
|
327
|
-
|
|
328
|
-
// we will assume onerror means we need to drop the events.
|
|
329
|
-
xdr.onerror = () => {
|
|
330
|
-
_doOnComplete(oncomplete, 400, {});
|
|
331
|
-
};
|
|
332
|
-
// we will assume ontimeout means we need to retry the events.
|
|
333
|
-
xdr.ontimeout = () => {
|
|
334
|
-
_doOnComplete(oncomplete, 500, {});
|
|
335
|
-
};
|
|
336
|
-
|
|
337
|
-
// https://cypressnorth.com/web-programming-and-development/internet-explorer-aborting-ajax-requests-fixed/
|
|
338
|
-
// tslint:disable-next-line:no-empty
|
|
339
|
-
xdr.onprogress = () => { };
|
|
340
|
-
|
|
341
|
-
if (sync) {
|
|
342
|
-
xdr.send(payload.data);
|
|
343
|
-
} else {
|
|
344
|
-
timeoutOverride.set(() => {
|
|
345
|
-
xdr.send(payload.data);
|
|
346
|
-
}, 0);
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
function _fetchSendPost(payload: IPayloadData, oncomplete: OnCompleteCallback, sync?: boolean) {
|
|
351
|
-
let theUrl = payload.urlString;
|
|
352
|
-
let ignoreResponse = false;
|
|
353
|
-
let responseHandled = false;
|
|
354
|
-
let requestInit: RequestInit = {
|
|
355
|
-
body: payload.data,
|
|
356
|
-
method: STR_POST_METHOD,
|
|
357
|
-
[STR_DISABLED_PROPERTY_NAME]: true
|
|
358
|
-
};
|
|
359
|
-
|
|
360
|
-
if (sync) {
|
|
361
|
-
requestInit.keepalive = true;
|
|
362
|
-
if ((payload as IInternalPayloadData)._sendReason === SendRequestReason.Unload) {
|
|
363
|
-
// As a sync request (during unload), it is unlikely that we will get a chance to process the response so
|
|
364
|
-
// just like beacon send assume that the events have been accepted and processed
|
|
365
|
-
ignoreResponse = true;
|
|
366
|
-
if (_addNoResponse) {
|
|
367
|
-
theUrl += _noResponseQs;
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
if (_sendCredentials) {
|
|
373
|
-
// Don't send credentials when URL is file://
|
|
374
|
-
requestInit.credentials = "include";
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
// Only add headers if there are headers to add, due to issue with some polyfills
|
|
378
|
-
if (payload.headers && objKeys(payload.headers).length > 0) {
|
|
379
|
-
requestInit.headers = payload.headers;
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
fetch(theUrl, requestInit).then((response) => {
|
|
383
|
-
let headerMap = {};
|
|
384
|
-
let responseText = STR_EMPTY;
|
|
385
|
-
var headers = response.headers;
|
|
386
|
-
if (headers) {
|
|
387
|
-
headers["forEach"]((value: string, name: string) => {
|
|
388
|
-
headerMap[name] = value;
|
|
389
|
-
});
|
|
390
|
-
}
|
|
391
|
-
if (response.body) {
|
|
392
|
-
response.text().then(function(text) {
|
|
393
|
-
responseText = text;
|
|
394
|
-
});
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
if (!responseHandled) {
|
|
398
|
-
responseHandled = true;
|
|
399
|
-
_doOnComplete(oncomplete, response.status, headerMap, responseText);
|
|
400
|
-
_handleCollectorResponse(responseText);
|
|
401
|
-
}
|
|
402
|
-
}).catch((error) => {
|
|
403
|
-
// In case there is an error in the request. Set the status to 0
|
|
404
|
-
// so that the events can be retried later.
|
|
405
|
-
if (!responseHandled) {
|
|
406
|
-
responseHandled = true;
|
|
407
|
-
_doOnComplete(oncomplete, 0, {});
|
|
408
|
-
}
|
|
409
|
-
});
|
|
410
|
-
|
|
411
|
-
if (ignoreResponse && !responseHandled) {
|
|
412
|
-
// Assume success during unload processing
|
|
413
|
-
responseHandled = true;
|
|
414
|
-
_doOnComplete(oncomplete, 200, {});
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
if (!responseHandled && payload.timeout > 0) {
|
|
418
|
-
// Simulate timeout
|
|
419
|
-
timeoutOverride.set(() => {
|
|
420
|
-
if (!responseHandled) {
|
|
421
|
-
// Assume a 500 response (which will cause a retry)
|
|
422
|
-
responseHandled = true;
|
|
423
|
-
_doOnComplete(oncomplete, 500, {});
|
|
424
|
-
}
|
|
425
|
-
}, payload.timeout);
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
function _xhrSendPost(payload: IPayloadData, oncomplete: OnCompleteCallback, sync?: boolean) {
|
|
430
|
-
let theUrl = payload.urlString;
|
|
431
|
-
|
|
432
|
-
function _appendHeader(theHeaders, xhr, name) {
|
|
433
|
-
if (!theHeaders[name] && xhr && xhr.getResponseHeader) {
|
|
434
|
-
let value = xhr.getResponseHeader(name);
|
|
435
|
-
if (value) {
|
|
436
|
-
theHeaders[name] = strTrim(value);
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
return theHeaders;
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
function _getAllResponseHeaders(xhr) {
|
|
444
|
-
let theHeaders = {};
|
|
445
|
-
|
|
446
|
-
if (!xhr.getAllResponseHeaders) {
|
|
447
|
-
// Firefox 2-63 doesn't have getAllResponseHeaders function but it does have getResponseHeader
|
|
448
|
-
// Only call these if getAllResponseHeaders doesn't exist, otherwise we can get invalid response errors
|
|
449
|
-
// as collector is not currently returning the correct header to allow JS to access these headers
|
|
450
|
-
theHeaders = _appendHeader(theHeaders, xhr, STR_TIME_DELTA_HEADER);
|
|
451
|
-
theHeaders = _appendHeader(theHeaders, xhr, STR_KILL_DURATION_HEADER);
|
|
452
|
-
theHeaders = _appendHeader(theHeaders, xhr, STR_KILL_DURATION_SECONDS_HEADER);
|
|
453
|
-
} else {
|
|
454
|
-
theHeaders = _convertAllHeadersToMap(xhr.getAllResponseHeaders());
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
return theHeaders;
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
function xhrComplete(xhr, responseTxt?) {
|
|
461
|
-
_doOnComplete(oncomplete, xhr.status, _getAllResponseHeaders(xhr), responseTxt);
|
|
462
|
-
}
|
|
463
|
-
if (sync && payload.disableXhrSync) {
|
|
464
|
-
sync = false;
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
let xhrRequest = openXhr(STR_POST_METHOD, theUrl, _sendCredentials, true, sync, payload.timeout);
|
|
468
|
-
|
|
469
|
-
// Set custom headers (e.g. gzip) here (after open())
|
|
470
|
-
objForEachKey(payload.headers, (name, value) => {
|
|
471
|
-
xhrRequest.setRequestHeader(name, value);
|
|
472
|
-
});
|
|
473
|
-
xhrRequest.onload = () => {
|
|
474
|
-
let response = _getResponseText(xhrRequest);
|
|
475
|
-
xhrComplete(xhrRequest, response);
|
|
476
|
-
_handleCollectorResponse(response);
|
|
477
|
-
};
|
|
478
|
-
xhrRequest.onerror = () => {
|
|
479
|
-
xhrComplete(xhrRequest);
|
|
480
|
-
};
|
|
481
|
-
xhrRequest.ontimeout = () => {
|
|
482
|
-
xhrComplete(xhrRequest);
|
|
483
|
-
};
|
|
484
|
-
xhrRequest.send(payload.data);
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
function _doOnComplete(oncomplete: OnCompleteCallback, status: number, headers: { [headerName: string]: string }, response?: string) {
|
|
488
|
-
try {
|
|
489
|
-
oncomplete(status, headers, response);
|
|
490
|
-
} catch (e) {
|
|
491
|
-
_throwInternal(_logger,
|
|
492
|
-
eLoggingSeverity.WARNING,
|
|
493
|
-
_eExtendedInternalMessageId.SendPostOnCompleteFailure, dumpObj(e));
|
|
494
|
-
}
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
function _beaconSendPost(payload: IPayloadData, oncomplete: OnCompleteCallback, sync?: boolean) {
|
|
498
|
-
// Custom headers not supported in sendBeacon payload.headers would be ignored
|
|
499
|
-
let internalPayloadData = payload as IInternalPayloadData;
|
|
500
|
-
let status = 200;
|
|
501
|
-
let thePayload = internalPayloadData._thePayload;
|
|
502
|
-
let theUrl = payload.urlString + (_addNoResponse ? _noResponseQs : STR_EMPTY);
|
|
503
|
-
|
|
504
|
-
try {
|
|
505
|
-
let nav = getNavigator();
|
|
506
|
-
if (!nav.sendBeacon(theUrl, payload.data)) {
|
|
507
|
-
if (thePayload) {
|
|
508
|
-
// Failed to send entire payload so try and split data and try to send as much events as possible
|
|
509
|
-
let droppedBatches: EventBatch[] = [];
|
|
510
|
-
arrForEach(thePayload.batches, (theBatch) => {
|
|
511
|
-
if (droppedBatches && theBatch && theBatch.count() > 0) {
|
|
512
|
-
let theEvents = theBatch.events();
|
|
513
|
-
for (let lp = 0; lp < theEvents.length; lp++) {
|
|
514
|
-
if (!nav.sendBeacon(theUrl, _serializer.getEventBlob(theEvents[lp]))) {
|
|
515
|
-
// Can't send anymore, so split the batch and drop the rest
|
|
516
|
-
droppedBatches.push(theBatch.split(lp));
|
|
517
|
-
break;
|
|
518
|
-
}
|
|
519
|
-
}
|
|
520
|
-
} else {
|
|
521
|
-
// Remove all of the events from the existing batch in the payload as the copy includes the original
|
|
522
|
-
droppedBatches.push(theBatch.split(0));
|
|
523
|
-
}
|
|
524
|
-
});
|
|
525
|
-
|
|
526
|
-
_sendBatchesNotification(droppedBatches, EventBatchNotificationReason.SizeLimitExceeded, thePayload.sendType, true);
|
|
527
|
-
} else {
|
|
528
|
-
status = 0;
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
} catch (ex) {
|
|
533
|
-
_warnToConsole(_logger, "Failed to send telemetry using sendBeacon API. Ex:" + dumpObj(ex));
|
|
534
|
-
status = 0;
|
|
535
|
-
} finally {
|
|
536
|
-
_doOnComplete(oncomplete, status, {}, STR_EMPTY);
|
|
537
|
-
}
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
function _isBeaconPayload(sendType: EventSendType) {
|
|
541
|
-
// Sync Fetch has the same payload limitation as sendBeacon -- 64kb limit, so treat both as a beacon send
|
|
542
|
-
return sendType === EventSendType.SendBeacon || sendType === EventSendType.SyncFetch;
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
function _adjustSendType(sendType: EventSendType) {
|
|
546
|
-
if (_isUnloading && _isBeaconPayload(sendType)) {
|
|
547
|
-
sendType = EventSendType.SendBeacon;
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
return sendType;
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
_self.addQueryStringParameter = (name: string, value: string) => {
|
|
554
|
-
for (let i = 0; i < _queryStringParameters.length; i++) {
|
|
555
|
-
if (_queryStringParameters[i].name === name) {
|
|
556
|
-
_queryStringParameters[i].value = value;
|
|
557
|
-
return;
|
|
558
|
-
}
|
|
559
|
-
}
|
|
560
|
-
_queryStringParameters.push({ name: name, value: value });
|
|
561
|
-
};
|
|
562
|
-
|
|
563
|
-
_self.addHeader = (name: string, value: string) => {
|
|
564
|
-
_headers[name] = value;
|
|
565
|
-
};
|
|
566
|
-
|
|
567
|
-
_self.canSendRequest = () => {
|
|
568
|
-
return _hasIdleConnection() && _clockSkewManager.allowRequestSending();
|
|
569
|
-
};
|
|
570
|
-
|
|
571
|
-
_self.sendQueuedRequests = (sendType?: EventSendType, sendReason?: SendRequestReason) => {
|
|
572
|
-
if (isUndefined(sendType)) {
|
|
573
|
-
sendType = EventSendType.Batched;
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
if (_isUnloading) {
|
|
577
|
-
sendType = _adjustSendType(sendType);
|
|
578
|
-
sendReason = SendRequestReason.Unload;
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
if (_canSendPayload(_batchQueue, sendType, 0)) {
|
|
582
|
-
_sendBatches(_clearQueue(), 0, false, sendType, sendReason || SendRequestReason.Undefined);
|
|
583
|
-
}
|
|
584
|
-
};
|
|
585
|
-
|
|
586
|
-
_self.isCompletelyIdle = (): boolean => {
|
|
587
|
-
return !_paused && _outstandingRequests === 0 && _batchQueue.length === 0;
|
|
588
|
-
};
|
|
589
|
-
|
|
590
|
-
_self.setUnloading = (value: boolean): void => {
|
|
591
|
-
_isUnloading = value;
|
|
592
|
-
};
|
|
593
|
-
|
|
594
|
-
_self.addBatch = (theBatch: EventBatch) => {
|
|
595
|
-
if (theBatch && theBatch.count() > 0) {
|
|
596
|
-
// Try and kill the event faster
|
|
597
|
-
if (_killSwitch.isTenantKilled(theBatch.iKey())) {
|
|
598
|
-
return false;
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
_batchQueue.push(theBatch);
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
return true;
|
|
605
|
-
};
|
|
606
|
-
|
|
607
|
-
/**
|
|
608
|
-
* Queue all the remaining requests to be sent. The requests will be
|
|
609
|
-
* sent using HTML5 Beacons if they are available.
|
|
610
|
-
*/
|
|
611
|
-
_self.teardown = () => {
|
|
612
|
-
if (_batchQueue.length > 0) {
|
|
613
|
-
_sendBatches(_clearQueue(), 0, true, EventSendType.SendBeacon, SendRequestReason.Unload);
|
|
614
|
-
}
|
|
615
|
-
};
|
|
616
|
-
|
|
617
|
-
/**
|
|
618
|
-
* Pause the sending of requests. No new requests will be sent.
|
|
619
|
-
*/
|
|
620
|
-
_self.pause = () => {
|
|
621
|
-
_paused = true;
|
|
622
|
-
};
|
|
623
|
-
|
|
624
|
-
/**
|
|
625
|
-
* Resume the sending of requests.
|
|
626
|
-
*/
|
|
627
|
-
_self.resume = () => {
|
|
628
|
-
_paused = false;
|
|
629
|
-
_self.sendQueuedRequests(EventSendType.Batched, SendRequestReason.Resumed);
|
|
630
|
-
};
|
|
631
|
-
|
|
632
|
-
/**
|
|
633
|
-
* Sends a request synchronously to the Aria collector. This api is used to send
|
|
634
|
-
* a request containing a single immediate event.
|
|
635
|
-
*
|
|
636
|
-
* @param batch - The request to be sent.
|
|
637
|
-
* @param sendReason - The token used to send the request.
|
|
638
|
-
*/
|
|
639
|
-
_self.sendSynchronousBatch = (batch: EventBatch, sendType?: EventSendType, sendReason?: SendRequestReason) => {
|
|
640
|
-
// This will not take into account the max connections restriction. Since this is sync, we can
|
|
641
|
-
// only send one of this request at a time and thus should not worry about multiple connections
|
|
642
|
-
// being used to send synchronous events.
|
|
643
|
-
// Increment active connection since we are still going to use a connection to send the request.
|
|
644
|
-
if (batch && batch.count() > 0) {
|
|
645
|
-
if (isNullOrUndefined(sendType)) {
|
|
646
|
-
sendType = EventSendType.Synchronous;
|
|
647
|
-
}
|
|
648
|
-
|
|
649
|
-
if (_isUnloading) {
|
|
650
|
-
sendType = _adjustSendType(sendType);
|
|
651
|
-
sendReason = SendRequestReason.Unload;
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
// For sync requests we will not wait for the clock skew.
|
|
655
|
-
_sendBatches([batch], 0, false, sendType, sendReason || SendRequestReason.Undefined);
|
|
656
|
-
}
|
|
657
|
-
};
|
|
658
|
-
|
|
659
|
-
function _hasIdleConnection(): boolean {
|
|
660
|
-
return !_paused && _outstandingRequests < maxConnections;
|
|
661
|
-
}
|
|
662
|
-
|
|
663
|
-
function _clearQueue() {
|
|
664
|
-
let theQueue = _batchQueue;
|
|
665
|
-
_batchQueue = [];
|
|
666
|
-
return theQueue;
|
|
667
|
-
}
|
|
668
|
-
|
|
669
|
-
function _canSendPayload(theBatches: EventBatch[], sendType: EventSendType, retryCnt: number) {
|
|
670
|
-
let result = false;
|
|
671
|
-
if (theBatches && theBatches.length > 0 && !_paused && _sendInterfaces[sendType] && _serializer) {
|
|
672
|
-
// Always attempt to send synchronous events don't wait for idle or clockSkew
|
|
673
|
-
// and don't block retry requests if clockSkew is not yet set
|
|
674
|
-
result = (sendType !== EventSendType.Batched) || (_hasIdleConnection() && (retryCnt > 0 || _clockSkewManager.allowRequestSending()));
|
|
675
|
-
}
|
|
676
|
-
|
|
677
|
-
return result;
|
|
678
|
-
}
|
|
679
|
-
|
|
680
|
-
function _createDebugBatches(theBatches: EventBatch[]) {
|
|
681
|
-
let values = {};
|
|
682
|
-
|
|
683
|
-
if (theBatches) {
|
|
684
|
-
arrForEach(theBatches, (theBatch, idx) => {
|
|
685
|
-
values[idx] = {
|
|
686
|
-
iKey: theBatch.iKey(),
|
|
687
|
-
evts: theBatch.events()
|
|
688
|
-
};
|
|
689
|
-
});
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
return values;
|
|
693
|
-
}
|
|
694
|
-
|
|
695
|
-
function _sendBatches(theBatches: EventBatch[], retryCount: number, isTeardown: boolean, sendType: EventSendType, sendReason: SendRequestReason) {
|
|
696
|
-
if (!theBatches || theBatches.length === 0) {
|
|
697
|
-
// Nothing to do
|
|
698
|
-
return;
|
|
699
|
-
}
|
|
700
|
-
|
|
701
|
-
if (_paused) {
|
|
702
|
-
_sendBatchesNotification(theBatches, EventBatchNotificationReason.Paused, sendType);
|
|
703
|
-
return;
|
|
704
|
-
}
|
|
705
|
-
|
|
706
|
-
// Make sure that if we are unloading the sendType is a supported version
|
|
707
|
-
sendType = _adjustSendType(sendType);
|
|
708
|
-
|
|
709
|
-
try {
|
|
710
|
-
let orgBatches = theBatches;
|
|
711
|
-
let isSynchronous = sendType !== EventSendType.Batched;
|
|
712
|
-
doPerf(_core, () => "HttpManager:_sendBatches", (perfEvt?: IPerfEvent) => {
|
|
713
|
-
if (perfEvt) {
|
|
714
|
-
// Perf Monitoring is enabled, so create a "Quick" copy of the original batches so we still report
|
|
715
|
-
// the original values as part of the perfEvent. This is because theBatches uses .shift() to remove each
|
|
716
|
-
// batch as they are processed - removing from the original array, so by the time the _createDebugBatches()
|
|
717
|
-
// function is called the passed in value has changed and therefore the reported value for the perfEvent is incorrect
|
|
718
|
-
theBatches = theBatches.slice(0);
|
|
719
|
-
}
|
|
720
|
-
|
|
721
|
-
let droppedBatches: EventBatch[] = [];
|
|
722
|
-
let thePayload: ISerializedPayload = null;
|
|
723
|
-
let serializationStart = getTime();
|
|
724
|
-
let sendInterface = _sendInterfaces[sendType] || (isSynchronous ? _sendInterfaces[EventSendType.Synchronous] : _sendInterfaces[EventSendType.Batched]);
|
|
725
|
-
let sendTransport = sendInterface && sendInterface._transport;
|
|
726
|
-
|
|
727
|
-
// Sync Fetch has the same payload limitation as sendBeacon -- 64kb limit
|
|
728
|
-
let isReducedPayload = _canHaveReducedPayload && (_isUnloading || _isBeaconPayload(sendType) || (sendTransport === TransportType.Beacon || (sendInterface._isSync && sendTransport === TransportType.Fetch)));
|
|
729
|
-
|
|
730
|
-
while (_canSendPayload(theBatches, sendType, retryCount)) {
|
|
731
|
-
let theBatch = theBatches.shift();
|
|
732
|
-
if (theBatch && theBatch.count() > 0) {
|
|
733
|
-
if (!_killSwitch.isTenantKilled(theBatch.iKey())) {
|
|
734
|
-
|
|
735
|
-
// Make sure we have a payload object
|
|
736
|
-
thePayload = thePayload || _serializer.createPayload(retryCount, isTeardown, isSynchronous, isReducedPayload, sendReason, sendType);
|
|
737
|
-
|
|
738
|
-
// Add the batch to the current payload
|
|
739
|
-
if (!_serializer.appendPayload(thePayload, theBatch, maxEventsPerBatch)) {
|
|
740
|
-
// Entire batch was not added so send the payload and retry adding this batch
|
|
741
|
-
_doPayloadSend(thePayload, serializationStart, getTime(), sendReason);
|
|
742
|
-
serializationStart = getTime();
|
|
743
|
-
theBatches = [theBatch].concat(theBatches);
|
|
744
|
-
thePayload = null;
|
|
745
|
-
} else if (thePayload.overflow !== null) {
|
|
746
|
-
// Total Payload size was exceeded so send the payload and add the unsent as the next batch to send
|
|
747
|
-
theBatches = [thePayload.overflow].concat(theBatches);
|
|
748
|
-
thePayload.overflow = null;
|
|
749
|
-
_doPayloadSend(thePayload, serializationStart, getTime(), sendReason);
|
|
750
|
-
serializationStart = getTime();
|
|
751
|
-
thePayload = null;
|
|
752
|
-
}
|
|
753
|
-
} else {
|
|
754
|
-
droppedBatches.push(theBatch);
|
|
755
|
-
}
|
|
756
|
-
}
|
|
757
|
-
}
|
|
758
|
-
|
|
759
|
-
// Make sure to flush any remaining payload
|
|
760
|
-
if (thePayload) {
|
|
761
|
-
_doPayloadSend(thePayload, serializationStart, getTime(), sendReason);
|
|
762
|
-
}
|
|
763
|
-
|
|
764
|
-
if (theBatches.length > 0) {
|
|
765
|
-
// Add any unsent batches back to the head of the queue
|
|
766
|
-
_batchQueue = theBatches.concat(_batchQueue);
|
|
767
|
-
}
|
|
768
|
-
|
|
769
|
-
// Now send notification about any dropped events
|
|
770
|
-
_sendBatchesNotification(droppedBatches, EventBatchNotificationReason.KillSwitch, sendType);
|
|
771
|
-
}, () => ({ batches: _createDebugBatches(orgBatches), retryCount, isTeardown, isSynchronous, sendReason, useSendBeacon: _isBeaconPayload(sendType), sendType }), !isSynchronous);
|
|
772
|
-
} catch (ex) {
|
|
773
|
-
_throwInternal(_logger,
|
|
774
|
-
eLoggingSeverity.WARNING,
|
|
775
|
-
_eInternalMessageId.CannotSerializeObject, "Unexpected Exception sending batch: " + dumpObj(ex));
|
|
776
|
-
}
|
|
777
|
-
}
|
|
778
|
-
|
|
779
|
-
function _buildRequestDetails(thePayload: ISerializedPayload, useHeaders: boolean): IRequestUrlDetails {
|
|
780
|
-
let requestDetails: IRequestUrlDetails = {
|
|
781
|
-
url: _urlString,
|
|
782
|
-
hdrs: {},
|
|
783
|
-
useHdrs: false // Assume no headers
|
|
784
|
-
};
|
|
785
|
-
|
|
786
|
-
if (!useHeaders) {
|
|
787
|
-
// Attempt to map headers to a query string if possible
|
|
788
|
-
objForEachKey(_headers, (name, value) => {
|
|
789
|
-
if (_collectorHeaderToQs[name]) {
|
|
790
|
-
_addRequestDetails(requestDetails, _collectorHeaderToQs[name], value, false);
|
|
791
|
-
} else {
|
|
792
|
-
// No mapping, so just include in the headers anyway (may not get sent if using sendBeacon())
|
|
793
|
-
requestDetails.hdrs[name] = value;
|
|
794
|
-
requestDetails.useHdrs = true;
|
|
795
|
-
}
|
|
796
|
-
});
|
|
797
|
-
} else {
|
|
798
|
-
// Copy the pre-defined headers into the payload headers
|
|
799
|
-
requestDetails.hdrs = extend(requestDetails.hdrs, _headers);
|
|
800
|
-
requestDetails.useHdrs = (objKeys(requestDetails.hdrs).length > 0);
|
|
801
|
-
}
|
|
802
|
-
|
|
803
|
-
_addRequestDetails(requestDetails, STR_CLIENT_ID, "NO_AUTH", useHeaders);
|
|
804
|
-
_addRequestDetails(requestDetails, STR_CLIENT_VERSION, FullVersionString, useHeaders);
|
|
805
|
-
|
|
806
|
-
let apiQsKeys = STR_EMPTY;
|
|
807
|
-
arrForEach(thePayload.apiKeys, (apiKey) => {
|
|
808
|
-
if (apiQsKeys.length > 0) {
|
|
809
|
-
apiQsKeys += ",";
|
|
810
|
-
}
|
|
811
|
-
|
|
812
|
-
apiQsKeys += apiKey;
|
|
813
|
-
});
|
|
814
|
-
|
|
815
|
-
_addRequestDetails(requestDetails, STR_API_KEY, apiQsKeys, useHeaders);
|
|
816
|
-
_addRequestDetails(requestDetails, STR_UPLOAD_TIME, dateNow().toString(), useHeaders);
|
|
817
|
-
|
|
818
|
-
let msfpc = _getMsfpc(thePayload);
|
|
819
|
-
if (isValueAssigned(msfpc)) {
|
|
820
|
-
requestDetails.url += "&ext.intweb.msfpc=" + msfpc;
|
|
821
|
-
}
|
|
822
|
-
|
|
823
|
-
if (_clockSkewManager.shouldAddClockSkewHeaders()) {
|
|
824
|
-
_addRequestDetails(requestDetails, STR_TIME_DELTA_TO_APPLY, _clockSkewManager.getClockSkewHeaderValue(), useHeaders);
|
|
825
|
-
}
|
|
826
|
-
|
|
827
|
-
if (_core.getWParam) {
|
|
828
|
-
let wParam = _core.getWParam();
|
|
829
|
-
if (wParam >= 0) {
|
|
830
|
-
requestDetails.url += "&w=" + wParam;
|
|
831
|
-
}
|
|
832
|
-
}
|
|
833
|
-
|
|
834
|
-
for (let i = 0; i < _queryStringParameters.length; i++) {
|
|
835
|
-
requestDetails.url += "&" + _queryStringParameters[i].name + "=" + _queryStringParameters[i].value;
|
|
836
|
-
}
|
|
837
|
-
|
|
838
|
-
return requestDetails;
|
|
839
|
-
}
|
|
840
|
-
|
|
841
|
-
function _setTimingValue(timings: any, name: string, value: number) {
|
|
842
|
-
timings[name] = timings[name] || {};
|
|
843
|
-
timings[name][_postManager.identifier] = value;
|
|
844
|
-
}
|
|
845
|
-
|
|
846
|
-
function _doPayloadSend(thePayload: ISerializedPayload, serializationStart: number, serializationCompleted: number, sendReason: SendRequestReason) {
|
|
847
|
-
|
|
848
|
-
if (thePayload && thePayload.payloadBlob && thePayload.payloadBlob.length > 0) {
|
|
849
|
-
let useSendHook = !!_self.sendHook;
|
|
850
|
-
let sendInterface = _sendInterfaces[thePayload.sendType];
|
|
851
|
-
|
|
852
|
-
// Send all data using a beacon style transport if closing mode is on or channel was teared down
|
|
853
|
-
if (!_isBeaconPayload(thePayload.sendType) && thePayload.isBeacon && thePayload.sendReason === SendRequestReason.Unload) {
|
|
854
|
-
sendInterface = _sendInterfaces[EventSendType.SendBeacon] || _sendInterfaces[EventSendType.SyncFetch] || sendInterface;
|
|
855
|
-
}
|
|
856
|
-
|
|
857
|
-
let useHeaders = _useHeaders;
|
|
858
|
-
|
|
859
|
-
// Disable header usage if we know we are using sendBeacon as additional headers are not supported
|
|
860
|
-
if (thePayload.isBeacon || sendInterface._transport === TransportType.Beacon) {
|
|
861
|
-
useHeaders = false;
|
|
862
|
-
}
|
|
863
|
-
|
|
864
|
-
let requestDetails = _buildRequestDetails(thePayload, useHeaders);
|
|
865
|
-
useHeaders = useHeaders || requestDetails.useHdrs;
|
|
866
|
-
|
|
867
|
-
let sendEventStart = getTime();
|
|
868
|
-
|
|
869
|
-
doPerf(_core, () => "HttpManager:_doPayloadSend", () => {
|
|
870
|
-
// Increment the send attempt count and add timings after packaging (So it's not serialized in the 1st attempt)
|
|
871
|
-
for (let batchLp = 0; batchLp < thePayload.batches.length; batchLp++) {
|
|
872
|
-
let theBatch = thePayload.batches[batchLp];
|
|
873
|
-
let theEvents: IPostTransmissionTelemetryItem[] = theBatch.events();
|
|
874
|
-
for (let evtLp = 0; evtLp < theEvents.length; evtLp++) {
|
|
875
|
-
let telemetryItem: IPostTransmissionTelemetryItem = theEvents[evtLp];
|
|
876
|
-
if (_enableEventTimings) {
|
|
877
|
-
let timings = telemetryItem.timings = telemetryItem.timings || {};
|
|
878
|
-
_setTimingValue(timings, "sendEventStart", sendEventStart);
|
|
879
|
-
_setTimingValue(timings, "serializationStart", serializationStart);
|
|
880
|
-
_setTimingValue(timings, "serializationCompleted", serializationCompleted);
|
|
881
|
-
}
|
|
882
|
-
|
|
883
|
-
telemetryItem[strSendAttempt] > 0 ? telemetryItem[strSendAttempt]++ : telemetryItem[strSendAttempt] = 1;
|
|
884
|
-
}
|
|
885
|
-
}
|
|
886
|
-
|
|
887
|
-
// Note: always sending this notification in a synchronous manner.
|
|
888
|
-
_sendBatchesNotification(
|
|
889
|
-
thePayload.batches,
|
|
890
|
-
(EventBatchNotificationReason.SendingUndefined + (sendReason || SendRequestReason.Undefined)),
|
|
891
|
-
thePayload.sendType,
|
|
892
|
-
true);
|
|
893
|
-
|
|
894
|
-
// Disabling the use of const because of Issue:
|
|
895
|
-
// - Task 9227844: [1DS] Some environments and packagers automatically "freeze" objects which are defined as const which causes any mutations to throw
|
|
896
|
-
// eslint-disable-next-line prefer-const
|
|
897
|
-
let orgPayloadData: IInternalPayloadData = {
|
|
898
|
-
data: thePayload.payloadBlob,
|
|
899
|
-
urlString: requestDetails.url,
|
|
900
|
-
headers: requestDetails.hdrs,
|
|
901
|
-
_thePayload: thePayload,
|
|
902
|
-
_sendReason: sendReason,
|
|
903
|
-
timeout: _xhrTimeout,
|
|
904
|
-
disableXhrSync: _disableXhrSync,
|
|
905
|
-
disableFetchKeepAlive: _disableFetchKeepAlive
|
|
906
|
-
};
|
|
907
|
-
|
|
908
|
-
// Only automatically add the following headers if already sending headers and we are not attempting to avoid an options call
|
|
909
|
-
if (useHeaders) {
|
|
910
|
-
if (!_hasHeader(orgPayloadData.headers, STR_CACHE_CONTROL)) {
|
|
911
|
-
orgPayloadData.headers[STR_CACHE_CONTROL] = DEFAULT_CACHE_CONTROL;
|
|
912
|
-
}
|
|
913
|
-
|
|
914
|
-
if (!_hasHeader(orgPayloadData.headers, STR_CONTENT_TYPE_HEADER)) {
|
|
915
|
-
orgPayloadData.headers[STR_CONTENT_TYPE_HEADER] = DEFAULT_CONTENT_TYPE;
|
|
916
|
-
}
|
|
917
|
-
}
|
|
918
|
-
|
|
919
|
-
let sender: (payload: IPayloadData) => void = null;
|
|
920
|
-
|
|
921
|
-
if (sendInterface) {
|
|
922
|
-
// Send sync requests if the request is immediate or we are tearing down telemetry.
|
|
923
|
-
sender = (payload: IPayloadData) => {
|
|
924
|
-
// Notify the clock skew manager that we are sending the first request (Potentially blocking all further requests)
|
|
925
|
-
_clockSkewManager.firstRequestSent();
|
|
926
|
-
|
|
927
|
-
let onComplete = (status, headers) => {
|
|
928
|
-
_retryRequestIfNeeded(status, headers, thePayload, sendReason);
|
|
929
|
-
};
|
|
930
|
-
|
|
931
|
-
let isSync = thePayload.isTeardown || thePayload.isSync;
|
|
932
|
-
try {
|
|
933
|
-
sendInterface.sendPOST(payload, onComplete, isSync);
|
|
934
|
-
if (_self.sendListener) {
|
|
935
|
-
// Send the original payload to the listener
|
|
936
|
-
_self.sendListener(orgPayloadData, payload, isSync, thePayload.isBeacon);
|
|
937
|
-
}
|
|
938
|
-
} catch (ex) {
|
|
939
|
-
_warnToConsole(_logger, "Unexpected exception sending payload. Ex:" + dumpObj(ex));
|
|
940
|
-
|
|
941
|
-
_doOnComplete(onComplete, 0, {});
|
|
942
|
-
}
|
|
943
|
-
};
|
|
944
|
-
}
|
|
945
|
-
|
|
946
|
-
doPerf(_core, () => "HttpManager:_doPayloadSend.sender", () => {
|
|
947
|
-
if (sender) {
|
|
948
|
-
if (thePayload.sendType === EventSendType.Batched) {
|
|
949
|
-
_outstandingRequests ++;
|
|
950
|
-
}
|
|
951
|
-
|
|
952
|
-
// Only call the hook if it's defined and we are not using sendBeacon as additional headers are not supported
|
|
953
|
-
if (useSendHook && !thePayload.isBeacon && sendInterface._transport !== TransportType.Beacon) {
|
|
954
|
-
// Create a new IPayloadData that is sent into the hook method, so that the hook method
|
|
955
|
-
// can't change the object references to the orgPayloadData (it can still change the content -- mainly the headers)
|
|
956
|
-
|
|
957
|
-
// Disabling the use of const because of Issue:
|
|
958
|
-
// - Task 9227844: [1DS] Some environments and packagers automatically "freeze" objects which are defined as const which causes any mutations to throw
|
|
959
|
-
// eslint-disable-next-line prefer-const
|
|
960
|
-
let hookData: IPayloadData = {
|
|
961
|
-
data: orgPayloadData.data,
|
|
962
|
-
urlString: orgPayloadData.urlString,
|
|
963
|
-
headers: extend({}, orgPayloadData.headers),
|
|
964
|
-
timeout: orgPayloadData.timeout,
|
|
965
|
-
disableXhrSync: orgPayloadData.disableXhrSync,
|
|
966
|
-
disableFetchKeepAlive: orgPayloadData.disableFetchKeepAlive
|
|
967
|
-
};
|
|
968
|
-
|
|
969
|
-
let senderCalled = false;
|
|
970
|
-
doPerf(_core, () => "HttpManager:_doPayloadSend.sendHook", () => {
|
|
971
|
-
try {
|
|
972
|
-
_self.sendHook(
|
|
973
|
-
hookData,
|
|
974
|
-
(payload: IInternalPayloadData) => {
|
|
975
|
-
senderCalled = true;
|
|
976
|
-
// Add back the internal properties
|
|
977
|
-
if (!_customHttpInterface && !payload._thePayload) {
|
|
978
|
-
payload._thePayload = payload._thePayload || orgPayloadData._thePayload;
|
|
979
|
-
payload._sendReason = payload._sendReason || orgPayloadData._sendReason;
|
|
980
|
-
}
|
|
981
|
-
|
|
982
|
-
sender(payload);
|
|
983
|
-
},
|
|
984
|
-
thePayload.isSync || thePayload.isTeardown);
|
|
985
|
-
} catch (ex) {
|
|
986
|
-
if (!senderCalled) {
|
|
987
|
-
// The hook never called the sender -- assume that it never will
|
|
988
|
-
sender(orgPayloadData);
|
|
989
|
-
}
|
|
990
|
-
}
|
|
991
|
-
});
|
|
992
|
-
} else {
|
|
993
|
-
sender(orgPayloadData);
|
|
994
|
-
}
|
|
995
|
-
}
|
|
996
|
-
});
|
|
997
|
-
|
|
998
|
-
}, () => ({ thePayload, serializationStart, serializationCompleted, sendReason }), thePayload.isSync);
|
|
999
|
-
}
|
|
1000
|
-
|
|
1001
|
-
if (thePayload.sizeExceed && thePayload.sizeExceed.length > 0) {
|
|
1002
|
-
// Ensure that we send any discard events for oversize events even when there was no payload to send
|
|
1003
|
-
_sendBatchesNotification(thePayload.sizeExceed, EventBatchNotificationReason.SizeLimitExceeded, thePayload.sendType);
|
|
1004
|
-
}
|
|
1005
|
-
|
|
1006
|
-
if (thePayload.failedEvts && thePayload.failedEvts.length > 0) {
|
|
1007
|
-
// Ensure that we send any discard events for events that could not be serialized even when there was no payload to send
|
|
1008
|
-
_sendBatchesNotification(thePayload.failedEvts, EventBatchNotificationReason.InvalidEvent, thePayload.sendType);
|
|
1009
|
-
}
|
|
1010
|
-
}
|
|
1011
|
-
|
|
1012
|
-
function _addEventCompletedTimings(theEvents: IPostTransmissionTelemetryItem[], sendEventCompleted: number) {
|
|
1013
|
-
if (_enableEventTimings) {
|
|
1014
|
-
arrForEach(theEvents, (theEvent) => {
|
|
1015
|
-
let timings = theEvent.timings = theEvent.timings || {};
|
|
1016
|
-
_setTimingValue(timings, "sendEventCompleted", sendEventCompleted);
|
|
1017
|
-
});
|
|
1018
|
-
}
|
|
1019
|
-
}
|
|
1020
|
-
|
|
1021
|
-
function _retryRequestIfNeeded(status: number, headers: { [headerName: string]: string }, thePayload: ISerializedPayload, sendReason: SendRequestReason) {
|
|
1022
|
-
let reason: EventBatchNotificationReason = EventBatchNotificationReason.ResponseFailure;
|
|
1023
|
-
let droppedBatches: EventBatch[] = null;
|
|
1024
|
-
let isRetrying = false;
|
|
1025
|
-
let backOffTrans = false;
|
|
1026
|
-
|
|
1027
|
-
try {
|
|
1028
|
-
let shouldRetry = true;
|
|
1029
|
-
|
|
1030
|
-
if (typeof status !== strUndefined) {
|
|
1031
|
-
if (headers) {
|
|
1032
|
-
_clockSkewManager.setClockSkew(headers[STR_TIME_DELTA_HEADER]);
|
|
1033
|
-
let killDuration = headers[STR_KILL_DURATION_HEADER] || headers["kill-duration-seconds"];
|
|
1034
|
-
arrForEach(_killSwitch.setKillSwitchTenants(headers[STR_KILL_TOKENS_HEADER], killDuration), (killToken) => {
|
|
1035
|
-
arrForEach(thePayload.batches, (theBatch) => {
|
|
1036
|
-
if (theBatch.iKey() === killToken) {
|
|
1037
|
-
// Make sure we have initialized the array
|
|
1038
|
-
droppedBatches = droppedBatches || [];
|
|
1039
|
-
|
|
1040
|
-
// Create a copy of the batch with all of the events (and more importantly the action functions)
|
|
1041
|
-
let removedEvents = theBatch.split(0);
|
|
1042
|
-
// And then remove the events for the payload batch and reduce the actual number of processed
|
|
1043
|
-
thePayload.numEvents -= removedEvents.count();
|
|
1044
|
-
droppedBatches.push(removedEvents);
|
|
1045
|
-
}
|
|
1046
|
-
});
|
|
1047
|
-
});
|
|
1048
|
-
}
|
|
1049
|
-
|
|
1050
|
-
// Disabling triple-equals rule to avoid httpOverrides from failing because they are returning a string value
|
|
1051
|
-
// tslint:disable-next-line:triple-equals
|
|
1052
|
-
if (status == 200 || status == 204) {
|
|
1053
|
-
// Response was successfully sent
|
|
1054
|
-
reason = EventBatchNotificationReason.Complete;
|
|
1055
|
-
return;
|
|
1056
|
-
}
|
|
1057
|
-
|
|
1058
|
-
if (!retryPolicyShouldRetryForStatus(status) || thePayload.numEvents <= 0) {
|
|
1059
|
-
// Only retry for specific response codes and if there is still events after kill switch processing
|
|
1060
|
-
shouldRetry = false;
|
|
1061
|
-
}
|
|
1062
|
-
|
|
1063
|
-
// Derive the notification response from the HttpStatus Code
|
|
1064
|
-
reason = EventBatchNotificationReason.ResponseFailure + (status % 1000);
|
|
1065
|
-
}
|
|
1066
|
-
|
|
1067
|
-
if (shouldRetry) {
|
|
1068
|
-
// The events should be retried -- so change notification to requeue them
|
|
1069
|
-
reason = EventBatchNotificationReason.RequeueEvents;
|
|
1070
|
-
let retryCount = thePayload.retryCnt;
|
|
1071
|
-
if (thePayload.sendType === EventSendType.Batched) {
|
|
1072
|
-
// attempt to resend the entire batch
|
|
1073
|
-
if (retryCount < maxRequestRetriesBeforeBackoff) {
|
|
1074
|
-
isRetrying = true;
|
|
1075
|
-
_doAction(() => {
|
|
1076
|
-
// try to resend the same batches
|
|
1077
|
-
if (thePayload.sendType === EventSendType.Batched) {
|
|
1078
|
-
// Reduce the outstanding request count (if this was an async request) as we didn't reduce the count
|
|
1079
|
-
// previously and we are about to reschedule our retry attempt and we want an attempt to send
|
|
1080
|
-
// to occur, it's also required to ensure that a follow up handleRequestFinished() call occurs
|
|
1081
|
-
_outstandingRequests--;
|
|
1082
|
-
}
|
|
1083
|
-
|
|
1084
|
-
_sendBatches(thePayload.batches, retryCount + 1, thePayload.isTeardown, _isUnloading ? EventSendType.SendBeacon : thePayload.sendType, SendRequestReason.Retry);
|
|
1085
|
-
}, _isUnloading, retryPolicyGetMillisToBackoffForRetry(retryCount));
|
|
1086
|
-
} else {
|
|
1087
|
-
backOffTrans = true;
|
|
1088
|
-
if (_isUnloading) {
|
|
1089
|
-
// we are unloading so don't try and requeue the events otherwise let the events get requeued and resent during the backoff sending
|
|
1090
|
-
// This will also cause the events to be purged based on the priority (if necessary)
|
|
1091
|
-
reason = EventBatchNotificationReason.NonRetryableStatus;
|
|
1092
|
-
}
|
|
1093
|
-
}
|
|
1094
|
-
}
|
|
1095
|
-
}
|
|
1096
|
-
|
|
1097
|
-
} finally {
|
|
1098
|
-
if (!isRetrying) {
|
|
1099
|
-
// Make sure the clockSkewManager doesn't blocking further sending of requests once we have a proper response
|
|
1100
|
-
// This won't override any previously sent clock Skew value
|
|
1101
|
-
_clockSkewManager.setClockSkew();
|
|
1102
|
-
|
|
1103
|
-
_handleRequestFinished(thePayload, reason, sendReason, backOffTrans);
|
|
1104
|
-
}
|
|
1105
|
-
|
|
1106
|
-
_sendBatchesNotification(droppedBatches, EventBatchNotificationReason.KillSwitch, thePayload.sendType);
|
|
1107
|
-
}
|
|
1108
|
-
}
|
|
1109
|
-
|
|
1110
|
-
function _handleRequestFinished(
|
|
1111
|
-
thePayload: ISerializedPayload,
|
|
1112
|
-
batchReason: EventBatchNotificationReason,
|
|
1113
|
-
sendReason: SendRequestReason,
|
|
1114
|
-
backOffTrans: boolean) {
|
|
1115
|
-
|
|
1116
|
-
try {
|
|
1117
|
-
if (backOffTrans) {
|
|
1118
|
-
// Slow down the transmission requests
|
|
1119
|
-
_postManager._backOffTransmission();
|
|
1120
|
-
}
|
|
1121
|
-
|
|
1122
|
-
if (batchReason === EventBatchNotificationReason.Complete) {
|
|
1123
|
-
if (!backOffTrans && !thePayload.isSync) {
|
|
1124
|
-
// We have a successful async response, so the lets open the floodgates
|
|
1125
|
-
// The reason for checking isSync is to avoid unblocking if beacon send occurred as it
|
|
1126
|
-
// doesn't wait for a response.
|
|
1127
|
-
_postManager._clearBackOff();
|
|
1128
|
-
}
|
|
1129
|
-
|
|
1130
|
-
_addCompleteTimings(thePayload.batches);
|
|
1131
|
-
}
|
|
1132
|
-
|
|
1133
|
-
// Send the notifications synchronously
|
|
1134
|
-
_sendBatchesNotification(thePayload.batches, batchReason, thePayload.sendType, true);
|
|
1135
|
-
|
|
1136
|
-
} finally {
|
|
1137
|
-
if (thePayload.sendType === EventSendType.Batched) {
|
|
1138
|
-
// we always need to decrement this value otherwise the httpmanager locks up and won't send any more events
|
|
1139
|
-
_outstandingRequests--;
|
|
1140
|
-
|
|
1141
|
-
// Don't try to send additional queued events if this is a retry operation as the retried
|
|
1142
|
-
// response will eventually call _handleRequestFinished for the retried event
|
|
1143
|
-
if (sendReason !== SendRequestReason.Retry) {
|
|
1144
|
-
// Try and send any other queued batched events
|
|
1145
|
-
_self.sendQueuedRequests(thePayload.sendType, sendReason);
|
|
1146
|
-
}
|
|
1147
|
-
}
|
|
1148
|
-
}
|
|
1149
|
-
}
|
|
1150
|
-
|
|
1151
|
-
function _addCompleteTimings(theBatches: EventBatch[]) {
|
|
1152
|
-
if (_enableEventTimings) {
|
|
1153
|
-
let sendEventCompleted = getTime();
|
|
1154
|
-
arrForEach(theBatches, (theBatch) => {
|
|
1155
|
-
if (theBatch && theBatch.count() > 0) {
|
|
1156
|
-
_addEventCompletedTimings(theBatch.events(), sendEventCompleted);
|
|
1157
|
-
}
|
|
1158
|
-
});
|
|
1159
|
-
}
|
|
1160
|
-
}
|
|
1161
|
-
|
|
1162
|
-
function _doAction(cb: VoidFunction, isSync: boolean, interval: number) {
|
|
1163
|
-
if (isSync) {
|
|
1164
|
-
cb();
|
|
1165
|
-
} else {
|
|
1166
|
-
timeoutOverride.set(cb, interval);
|
|
1167
|
-
}
|
|
1168
|
-
}
|
|
1169
|
-
|
|
1170
|
-
/**
|
|
1171
|
-
* Converts the XHR getAllResponseHeaders to a map containing the header key and value.
|
|
1172
|
-
*/
|
|
1173
|
-
// tslint:disable-next-line: align
|
|
1174
|
-
function _convertAllHeadersToMap(headersString: string): { [headerName: string]: string } {
|
|
1175
|
-
let headers = {};
|
|
1176
|
-
if (isString(headersString)) {
|
|
1177
|
-
let headersArray = strTrim(headersString).split(/[\r\n]+/);
|
|
1178
|
-
arrForEach(headersArray, (headerEntry) => {
|
|
1179
|
-
if (headerEntry) {
|
|
1180
|
-
let idx = headerEntry.indexOf(": ");
|
|
1181
|
-
if (idx !== -1) {
|
|
1182
|
-
// The new spec has the headers returning all as lowercase -- but not all browsers do this yet
|
|
1183
|
-
let header = strTrim(headerEntry.substring(0, idx)).toLowerCase();
|
|
1184
|
-
let value = strTrim(headerEntry.substring(idx + 1));
|
|
1185
|
-
headers[header] = value;
|
|
1186
|
-
} else {
|
|
1187
|
-
headers[strTrim(headerEntry)] = 1;
|
|
1188
|
-
}
|
|
1189
|
-
}
|
|
1190
|
-
});
|
|
1191
|
-
}
|
|
1192
|
-
|
|
1193
|
-
return headers;
|
|
1194
|
-
}
|
|
1195
|
-
|
|
1196
|
-
function _getMsfpc(thePayload: ISerializedPayload): string {
|
|
1197
|
-
for (let lp = 0; lp < thePayload.batches.length; lp++) {
|
|
1198
|
-
let msfpc = thePayload.batches[lp].Msfpc();
|
|
1199
|
-
if (msfpc) {
|
|
1200
|
-
return encodeURIComponent(msfpc);
|
|
1201
|
-
}
|
|
1202
|
-
}
|
|
1203
|
-
|
|
1204
|
-
return STR_EMPTY;
|
|
1205
|
-
}
|
|
1206
|
-
|
|
1207
|
-
function _handleCollectorResponse(responseText: string): void {
|
|
1208
|
-
let responseHandlers = _self._responseHandlers;
|
|
1209
|
-
try {
|
|
1210
|
-
for (let i = 0; i < responseHandlers.length; i++) {
|
|
1211
|
-
try {
|
|
1212
|
-
responseHandlers[i](responseText);
|
|
1213
|
-
} catch (e) {
|
|
1214
|
-
_throwInternal(_logger,
|
|
1215
|
-
eLoggingSeverity.CRITICAL,
|
|
1216
|
-
_eExtendedInternalMessageId.PostResponseHandler,
|
|
1217
|
-
"Response handler failed: " + e);
|
|
1218
|
-
}
|
|
1219
|
-
}
|
|
1220
|
-
if (responseText) {
|
|
1221
|
-
let response = JSON.parse(responseText) as ICollectorResult;
|
|
1222
|
-
if (isValueAssigned(response.webResult) && isValueAssigned(response.webResult[STR_MSFPC])) {
|
|
1223
|
-
// Set cookie
|
|
1224
|
-
_cookieMgr.set("MSFPC", response.webResult[STR_MSFPC], 365 * 86400);
|
|
1225
|
-
}
|
|
1226
|
-
}
|
|
1227
|
-
} catch (ex) {
|
|
1228
|
-
// Doing nothing
|
|
1229
|
-
}
|
|
1230
|
-
}
|
|
1231
|
-
|
|
1232
|
-
function _sendBatchesNotification(theBatches: EventBatch[], batchReason: EventBatchNotificationReason, sendType: EventSendType, sendSync?: boolean) {
|
|
1233
|
-
if (theBatches && theBatches.length > 0 && actions) {
|
|
1234
|
-
let theAction: BatchNotificationAction = actions[_getNotificationAction(batchReason)];
|
|
1235
|
-
if (theAction) {
|
|
1236
|
-
let isSyncRequest = sendType !== EventSendType.Batched;
|
|
1237
|
-
|
|
1238
|
-
doPerf(_core, () => "HttpManager:_sendBatchesNotification", () => {
|
|
1239
|
-
_doAction(() => {
|
|
1240
|
-
try {
|
|
1241
|
-
theAction.call(actions, theBatches, batchReason, isSyncRequest, sendType);
|
|
1242
|
-
} catch (e) {
|
|
1243
|
-
_throwInternal(_logger,
|
|
1244
|
-
eLoggingSeverity.CRITICAL,
|
|
1245
|
-
_eInternalMessageId.NotificationException,
|
|
1246
|
-
"send request notification failed: " + e);
|
|
1247
|
-
}
|
|
1248
|
-
}, sendSync || isSyncRequest, 0);
|
|
1249
|
-
}, () => ({ batches: _createDebugBatches(theBatches), reason: batchReason, isSync: isSyncRequest, sendSync: sendSync, sendType: sendType }), !isSyncRequest);
|
|
1250
|
-
}
|
|
1251
|
-
}
|
|
1252
|
-
}
|
|
1253
|
-
|
|
1254
|
-
function _getNotificationAction(reason: EventBatchNotificationReason): string {
|
|
1255
|
-
let action = _eventActionMap[reason];
|
|
1256
|
-
if (!isValueAssigned(action)) {
|
|
1257
|
-
action = STR_OTHER;
|
|
1258
|
-
if (reason >= EventBatchNotificationReason.ResponseFailure && reason <= EventBatchNotificationReason.ResponseFailureMax) {
|
|
1259
|
-
action = STR_RESPONSE_FAIL;
|
|
1260
|
-
} else if (reason >= EventBatchNotificationReason.EventsDropped && reason <= EventBatchNotificationReason.EventsDroppedMax) {
|
|
1261
|
-
action = STR_DROPPED;
|
|
1262
|
-
} else if (reason >= EventBatchNotificationReason.SendingUndefined && reason <= EventBatchNotificationReason.SendingEventMax) {
|
|
1263
|
-
action = STR_SENDING;
|
|
1264
|
-
}
|
|
1265
|
-
}
|
|
1266
|
-
|
|
1267
|
-
return action;
|
|
1268
|
-
}
|
|
1269
|
-
});
|
|
1270
|
-
}
|
|
1271
|
-
|
|
1272
|
-
/**
|
|
1273
|
-
* @constructor
|
|
1274
|
-
* @param requestQueue - The queue that contains the requests to be sent.
|
|
1275
|
-
* @param endpointUrl - The collector url to which the requests must be sent.
|
|
1276
|
-
* @param postManager - The post manager that we should add requests back to if needed.
|
|
1277
|
-
* @param httpInterface - The http interface that should be used to send HTTP requests.
|
|
1278
|
-
* @param channelConfig - The IChannelConfiguration the should be used to get additional configuration
|
|
1279
|
-
*/
|
|
1280
|
-
public initialize(endpointUrl: string, core: IExtendedAppInsightsCore, postChannel: IPostChannel, httpInterface: IXHROverride, channelConfig?: IChannelConfiguration) {
|
|
1281
|
-
// @DynamicProtoStub - DO NOT add any code as this will be removed during packaging
|
|
1282
|
-
}
|
|
1283
|
-
|
|
1284
|
-
/**
|
|
1285
|
-
* Add query string parameter to url
|
|
1286
|
-
* @param name - Query string name.
|
|
1287
|
-
* @param value - Query string value.
|
|
1288
|
-
*/
|
|
1289
|
-
public addQueryStringParameter(name: string, value: string) {
|
|
1290
|
-
// @DynamicProtoStub - DO NOT add any code as this will be removed during packaging
|
|
1291
|
-
}
|
|
1292
|
-
|
|
1293
|
-
/**
|
|
1294
|
-
* Add header to request
|
|
1295
|
-
* @param name - Header name.
|
|
1296
|
-
* @param value - Header value.
|
|
1297
|
-
*/
|
|
1298
|
-
public addHeader(name: string, value: string) {
|
|
1299
|
-
// @DynamicProtoStub - DO NOT add any code as this will be removed during packaging
|
|
1300
|
-
}
|
|
1301
|
-
|
|
1302
|
-
/**
|
|
1303
|
-
* Add the batch of events to the queue for sending
|
|
1304
|
-
* @param batch The batch with the events to send
|
|
1305
|
-
* @returns True if the http manager has accepted the batch (including if the batch is empty) otherwise false
|
|
1306
|
-
*/
|
|
1307
|
-
public addBatch(batch: EventBatch): boolean {
|
|
1308
|
-
// @DynamicProtoStub - DO NOT add any code as this will be removed during packaging
|
|
1309
|
-
return false;
|
|
1310
|
-
}
|
|
1311
|
-
|
|
1312
|
-
/**
|
|
1313
|
-
* Check if there is an idle connection and we can send a request.
|
|
1314
|
-
* @returns True if there is an idle connection, false otherwise.
|
|
1315
|
-
*/
|
|
1316
|
-
public canSendRequest(): boolean {
|
|
1317
|
-
// @DynamicProtoStub - DO NOT add any code as this will be removed during packaging
|
|
1318
|
-
return false;
|
|
1319
|
-
}
|
|
1320
|
-
|
|
1321
|
-
/**
|
|
1322
|
-
* Send requests in the request queue up if there is an idle connection, sending is
|
|
1323
|
-
* not pause and clock skew manager allows sending request.
|
|
1324
|
-
* @param sendType - Identifies how the batched events should be send, defaults to Batched
|
|
1325
|
-
* @param sendReason - The reason the batch is being sent
|
|
1326
|
-
*/
|
|
1327
|
-
public sendQueuedRequests(sendType?: EventSendType, sendReason?: SendRequestReason) {
|
|
1328
|
-
// @DynamicProtoStub - DO NOT add any code as this will be removed during packaging
|
|
1329
|
-
}
|
|
1330
|
-
|
|
1331
|
-
/**
|
|
1332
|
-
* Check if there are no active requests being sent.
|
|
1333
|
-
* @returns True if idle, false otherwise.
|
|
1334
|
-
*/
|
|
1335
|
-
public isCompletelyIdle(): boolean {
|
|
1336
|
-
// @DynamicProtoStub - DO NOT add any code as this will be removed during packaging
|
|
1337
|
-
return false;
|
|
1338
|
-
}
|
|
1339
|
-
|
|
1340
|
-
/**
|
|
1341
|
-
* Inform the HttpManager that a page unload event was received
|
|
1342
|
-
*/
|
|
1343
|
-
public setUnloading(value: boolean): void {
|
|
1344
|
-
// @DynamicProtoStub - DO NOT add any code as this will be removed during packaging
|
|
1345
|
-
}
|
|
1346
|
-
|
|
1347
|
-
/**
|
|
1348
|
-
* Queue all the remaining requests to be sent. The requests will be
|
|
1349
|
-
* sent using HTML5 Beacons if they are available.
|
|
1350
|
-
*/
|
|
1351
|
-
public teardown() {
|
|
1352
|
-
// @DynamicProtoStub - DO NOT add any code as this will be removed during packaging
|
|
1353
|
-
}
|
|
1354
|
-
|
|
1355
|
-
/**
|
|
1356
|
-
* Pause the sending of requests. No new requests will be sent.
|
|
1357
|
-
*/
|
|
1358
|
-
public pause() {
|
|
1359
|
-
// @DynamicProtoStub - DO NOT add any code as this will be removed during packaging
|
|
1360
|
-
}
|
|
1361
|
-
|
|
1362
|
-
/**
|
|
1363
|
-
* Resume the sending of requests.
|
|
1364
|
-
*/
|
|
1365
|
-
public resume() {
|
|
1366
|
-
// @DynamicProtoStub - DO NOT add any code as this will be removed during packaging
|
|
1367
|
-
}
|
|
1368
|
-
|
|
1369
|
-
/**
|
|
1370
|
-
* Sends the batches synchronously to the collector. This api is used to send a batches immediate event.
|
|
1371
|
-
*
|
|
1372
|
-
* @param batch - The batch of events to be sent.
|
|
1373
|
-
* @param sendReason - The reason the batch is being sent
|
|
1374
|
-
* @param sendType - Identifies the sending type to use when sending the batch
|
|
1375
|
-
*/
|
|
1376
|
-
public sendSynchronousBatch(batch: EventBatch, sendType?: EventSendType, sendReason?: SendRequestReason) {
|
|
1377
|
-
// @DynamicProtoStub - DO NOT add any code as this will be removed during packaging
|
|
1378
|
-
}
|
|
1379
|
-
}
|