@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.
Files changed (105) hide show
  1. package/README.md +1 -1
  2. package/bundle/es5/ms.post-4.0.1.gbl.js +5918 -0
  3. package/bundle/es5/ms.post-4.0.1.gbl.js.map +1 -0
  4. package/bundle/es5/ms.post-4.0.1.gbl.min.js +7 -0
  5. package/bundle/es5/ms.post-4.0.1.gbl.min.js.map +1 -0
  6. package/bundle/es5/ms.post-4.0.1.integrity.json +46 -0
  7. package/bundle/es5/ms.post-4.0.1.js +5922 -0
  8. package/bundle/es5/ms.post-4.0.1.js.map +1 -0
  9. package/bundle/es5/ms.post-4.0.1.min.js +7 -0
  10. package/bundle/es5/ms.post-4.0.1.min.js.map +1 -0
  11. package/bundle/es5/ms.post.gbl.js +5918 -0
  12. package/bundle/es5/ms.post.gbl.js.map +1 -0
  13. package/bundle/es5/ms.post.gbl.min.js +7 -0
  14. package/bundle/es5/ms.post.gbl.min.js.map +1 -0
  15. package/bundle/es5/ms.post.integrity.json +46 -0
  16. package/bundle/es5/ms.post.js +5922 -0
  17. package/bundle/es5/ms.post.js.map +1 -0
  18. package/bundle/es5/ms.post.min.js +7 -0
  19. package/bundle/es5/ms.post.min.js.map +1 -0
  20. package/{bundle → dist/es5}/ms.post.js +3026 -1652
  21. package/dist/es5/ms.post.js.map +1 -0
  22. package/dist/es5/ms.post.min.js +7 -0
  23. package/dist/es5/ms.post.min.js.map +1 -0
  24. package/{dist-esm/src → dist-es5}/BatchNotificationActions.js +1 -1
  25. package/{dist-esm/src → dist-es5}/ClockSkewManager.js +2 -2
  26. package/{dist-esm/src → dist-es5}/ClockSkewManager.js.map +1 -1
  27. package/{dist-esm/src → dist-es5}/DataModels.js +1 -1
  28. package/{dist-esm/src → dist-es5}/EventBatch.js +1 -1
  29. package/{dist-esm/src → dist-es5}/HttpManager.js +175 -94
  30. package/dist-es5/HttpManager.js.map +1 -0
  31. package/{dist-esm/src → dist-es5}/Index.js +2 -2
  32. package/dist-es5/Index.js.map +1 -0
  33. package/{dist-esm/src → dist-es5}/InternalConstants.js +1 -1
  34. package/{dist-esm/src → dist-es5}/KillSwitch.js +2 -2
  35. package/{dist-esm/src → dist-es5}/KillSwitch.js.map +1 -1
  36. package/{dist-esm/src → dist-es5}/PostChannel.js +166 -144
  37. package/dist-es5/PostChannel.js.map +1 -0
  38. package/{dist-esm/src → dist-es5}/RetryPolicy.js +1 -1
  39. package/{dist-esm/src → dist-es5}/Serializer.js +3 -2
  40. package/dist-es5/Serializer.js.map +1 -0
  41. package/dist-es5/TimeoutOverrideWrapper.js +24 -0
  42. package/dist-es5/TimeoutOverrideWrapper.js.map +1 -0
  43. package/{dist-esm/src → dist-es5}/typings/XDomainRequest.js +1 -1
  44. package/package.json +15 -10
  45. package/tsconfig.json +5 -2
  46. package/{src/DataModels.ts → types/1ds-post-js.d.ts} +408 -467
  47. package/types/1ds-post-js.namespaced.d.ts +404 -0
  48. package/bundle/ms.post-3.2.12.gbl.js +0 -4524
  49. package/bundle/ms.post-3.2.12.gbl.js.map +0 -1
  50. package/bundle/ms.post-3.2.12.gbl.min.js +0 -7
  51. package/bundle/ms.post-3.2.12.gbl.min.js.map +0 -1
  52. package/bundle/ms.post-3.2.12.integrity.json +0 -46
  53. package/bundle/ms.post-3.2.12.js +0 -4527
  54. package/bundle/ms.post-3.2.12.js.map +0 -1
  55. package/bundle/ms.post-3.2.12.min.js +0 -7
  56. package/bundle/ms.post-3.2.12.min.js.map +0 -1
  57. package/bundle/ms.post.gbl.js +0 -4524
  58. package/bundle/ms.post.gbl.js.map +0 -1
  59. package/bundle/ms.post.gbl.min.js +0 -7
  60. package/bundle/ms.post.gbl.min.js.map +0 -1
  61. package/bundle/ms.post.integrity.json +0 -46
  62. package/bundle/ms.post.js.map +0 -1
  63. package/bundle/ms.post.min.js +0 -7
  64. package/bundle/ms.post.min.js.map +0 -1
  65. package/dist/ms.post.js +0 -2144
  66. package/dist/ms.post.js.map +0 -1
  67. package/dist/ms.post.min.js +0 -7
  68. package/dist/ms.post.min.js.map +0 -1
  69. package/dist-esm/src/BatchNotificationActions.d.ts +0 -36
  70. package/dist-esm/src/ClockSkewManager.d.ts +0 -38
  71. package/dist-esm/src/DataModels.d.ts +0 -405
  72. package/dist-esm/src/EventBatch.d.ts +0 -47
  73. package/dist-esm/src/HttpManager.d.ts +0 -88
  74. package/dist-esm/src/HttpManager.js.map +0 -1
  75. package/dist-esm/src/Index.d.ts +0 -9
  76. package/dist-esm/src/Index.js.map +0 -1
  77. package/dist-esm/src/InternalConstants.d.ts +0 -28
  78. package/dist-esm/src/KillSwitch.d.ts +0 -26
  79. package/dist-esm/src/PostChannel.d.ts +0 -101
  80. package/dist-esm/src/PostChannel.js.map +0 -1
  81. package/dist-esm/src/RetryPolicy.d.ts +0 -21
  82. package/dist-esm/src/Serializer.d.ts +0 -108
  83. package/dist-esm/src/Serializer.js.map +0 -1
  84. package/dist-esm/src/TimeoutOverrideWrapper.d.ts +0 -18
  85. package/dist-esm/src/TimeoutOverrideWrapper.js +0 -28
  86. package/dist-esm/src/TimeoutOverrideWrapper.js.map +0 -1
  87. package/dist-esm/src/typings/XDomainRequest.d.ts +0 -17
  88. package/src/BatchNotificationActions.ts +0 -44
  89. package/src/ClockSkewManager.ts +0 -127
  90. package/src/EventBatch.ts +0 -137
  91. package/src/HttpManager.ts +0 -1379
  92. package/src/Index.ts +0 -18
  93. package/src/InternalConstants.ts +0 -42
  94. package/src/KillSwitch.ts +0 -84
  95. package/src/PostChannel.ts +0 -1163
  96. package/src/RetryPolicy.ts +0 -46
  97. package/src/Serializer.ts +0 -487
  98. package/src/TimeoutOverrideWrapper.ts +0 -29
  99. package/src/typings/XDomainRequest.ts +0 -23
  100. /package/{dist-esm/src → dist-es5}/BatchNotificationActions.js.map +0 -0
  101. /package/{dist-esm/src → dist-es5}/DataModels.js.map +0 -0
  102. /package/{dist-esm/src → dist-es5}/EventBatch.js.map +0 -0
  103. /package/{dist-esm/src → dist-es5}/InternalConstants.js.map +0 -0
  104. /package/{dist-esm/src → dist-es5}/RetryPolicy.js.map +0 -0
  105. /package/{dist-esm/src → dist-es5}/typings/XDomainRequest.js.map +0 -0
@@ -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
- }