@microsoft/1ds-post-js 3.1.9 → 3.2.0

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 (60) hide show
  1. package/README.md +124 -5
  2. package/bundle/{ms.post-3.1.9.gbl.js → ms.post-3.2.0.gbl.js} +1706 -825
  3. package/bundle/ms.post-3.2.0.gbl.js.map +1 -0
  4. package/bundle/ms.post-3.2.0.gbl.min.js +7 -0
  5. package/bundle/ms.post-3.2.0.gbl.min.js.map +1 -0
  6. package/bundle/ms.post-3.2.0.integrity.json +46 -0
  7. package/bundle/{ms.post-3.1.9.js → ms.post-3.2.0.js} +1706 -825
  8. package/bundle/ms.post-3.2.0.js.map +1 -0
  9. package/bundle/ms.post-3.2.0.min.js +7 -0
  10. package/bundle/ms.post-3.2.0.min.js.map +1 -0
  11. package/bundle/ms.post.gbl.js +1705 -824
  12. package/bundle/ms.post.gbl.js.map +1 -1
  13. package/bundle/ms.post.gbl.min.js +2 -2
  14. package/bundle/ms.post.gbl.min.js.map +1 -1
  15. package/bundle/ms.post.integrity.json +17 -17
  16. package/bundle/ms.post.js +1705 -824
  17. package/bundle/ms.post.js.map +1 -1
  18. package/bundle/ms.post.min.js +2 -2
  19. package/bundle/ms.post.min.js.map +1 -1
  20. package/dist/ms.post.js +395 -212
  21. package/dist/ms.post.js.map +1 -1
  22. package/dist/ms.post.min.js +2 -2
  23. package/dist/ms.post.min.js.map +1 -1
  24. package/dist-esm/src/BatchNotificationActions.js +1 -1
  25. package/dist-esm/src/ClockSkewManager.js +1 -1
  26. package/dist-esm/src/Constants.d.ts +25 -0
  27. package/dist-esm/src/Constants.js +31 -0
  28. package/dist-esm/src/Constants.js.map +1 -0
  29. package/dist-esm/src/DataModels.d.ts +55 -0
  30. package/dist-esm/src/DataModels.js +1 -1
  31. package/dist-esm/src/EventBatch.d.ts +5 -2
  32. package/dist-esm/src/EventBatch.js +35 -15
  33. package/dist-esm/src/EventBatch.js.map +1 -1
  34. package/dist-esm/src/HttpManager.d.ts +2 -2
  35. package/dist-esm/src/HttpManager.js +186 -94
  36. package/dist-esm/src/HttpManager.js.map +1 -1
  37. package/dist-esm/src/Index.js +1 -1
  38. package/dist-esm/src/KillSwitch.js +1 -1
  39. package/dist-esm/src/PostChannel.d.ts +0 -4
  40. package/dist-esm/src/PostChannel.js +175 -107
  41. package/dist-esm/src/PostChannel.js.map +1 -1
  42. package/dist-esm/src/RetryPolicy.d.ts +20 -25
  43. package/dist-esm/src/RetryPolicy.js +35 -44
  44. package/dist-esm/src/RetryPolicy.js.map +1 -1
  45. package/dist-esm/src/Serializer.js +1 -1
  46. package/dist-esm/src/typings/XDomainRequest.js +1 -1
  47. package/package.json +3 -3
  48. package/src/Constants.ts +28 -0
  49. package/src/DataModels.ts +68 -0
  50. package/src/EventBatch.ts +47 -14
  51. package/src/HttpManager.ts +216 -98
  52. package/src/PostChannel.ts +207 -130
  53. package/src/RetryPolicy.ts +33 -38
  54. package/bundle/ms.post-3.1.9.gbl.js.map +0 -1
  55. package/bundle/ms.post-3.1.9.gbl.min.js +0 -7
  56. package/bundle/ms.post-3.1.9.gbl.min.js.map +0 -1
  57. package/bundle/ms.post-3.1.9.integrity.json +0 -46
  58. package/bundle/ms.post-3.1.9.js.map +0 -1
  59. package/bundle/ms.post-3.1.9.min.js +0 -7
  60. package/bundle/ms.post-3.1.9.min.js.map +0 -1
@@ -6,9 +6,9 @@
6
6
  import {
7
7
  isReactNative, isValueAssigned, isString, getTime, arrForEach, getLocation, strTrim, isFetchSupported, isXhrSupported,
8
8
  isBeaconsSupported, FullVersionString, useXDomainRequest, IExtendedAppInsightsCore, IExtendedConfiguration,
9
- LoggingSeverity, _ExtendedInternalMessageId, SendRequestReason, strUndefined, getNavigator, doPerf, IPerfEvent,
9
+ eLoggingSeverity, _eExtendedInternalMessageId, SendRequestReason, strUndefined, getNavigator, doPerf, IPerfEvent,
10
10
  dateNow, isUndefined, isNullOrUndefined, objForEachKey, ICookieMgr, isNumber, isArray, TransportType, dumpObj, objKeys, extend,
11
- EventSendType, hasOwnProperty, openXhr
11
+ EventSendType, hasOwnProperty, openXhr, _throwInternal, _eInternalMessageId
12
12
  } from "@microsoft/1ds-core-js";
13
13
  import {
14
14
  IXHROverride, IPostTransmissionTelemetryItem, ICollectorResult,
@@ -18,12 +18,22 @@ import {
18
18
  import { BatchNotificationAction, BatchNotificationActions } from "./BatchNotificationActions";
19
19
  import { EventBatch } from "./EventBatch";
20
20
  import { Serializer, ISerializedPayload } from "./Serializer";
21
- import RetryPolicy from "./RetryPolicy";
21
+ import { retryPolicyGetMillisToBackoffForRetry, retryPolicyShouldRetryForStatus } from "./RetryPolicy";
22
22
  import EVTKillSwitch from "./KillSwitch";
23
23
  import EVTClockSkewManager from "./ClockSkewManager";
24
24
  import dynamicProto from "@microsoft/dynamicproto-js";
25
25
  import { IChannelConfiguration } from "./Index";
26
26
  import { XDomainRequest as IXDomainRequest } from "./typings/XDomainRequest";
27
+ import {
28
+ defaultCacheControl, defaultContentType, DisabledPropertyName, Method, strApiKey, strAuthXToken, strCacheControl,
29
+ strClientId, strClientVersion, strContentTypeHeader, strDropped, strKillDurationHeader, strKillDurationSecondsHeader,
30
+ strKillTokensHeader, strMsaDeviceTicket, strMsfpc, strNoResponseBody, strOther, strRequeue, strResponseFail, strSending, strTimeDeltaHeader,
31
+ strTimeDeltaToApply, strUploadTime
32
+ } from "./Constants";
33
+
34
+ const strSendAttempt = "sendAttempt";
35
+
36
+ const _noResponseQs = "&" + strNoResponseBody + "=true";
27
37
 
28
38
  // TypeScript removed this interface so we need to declare the global so we can check for it's existence.
29
39
  declare var XDomainRequest: {
@@ -31,23 +41,11 @@ declare var XDomainRequest: {
31
41
  new(): IXDomainRequest;
32
42
  };
33
43
 
34
- const Method = "POST";
35
- const DisabledPropertyName: string = "Microsoft_ApplicationInsights_BypassAjaxInstrumentation";
36
-
37
- const strDropped = "drop";
38
- const strSending = "send";
39
- const strRequeue = "requeue";
40
- const strResponseFail = "rspFail";
41
- const strOther = "oth";
42
-
43
- const defaultCacheControl = "no-cache, no-store";
44
- const defaultContentType = "application/x-json-stream";
45
- const strCacheControl = "cache-control";
46
- const strContentTypeHeader = "content-type";
47
- const strKillTokensHeader = "kill-tokens";
48
- const strKillDurationHeader = "kill-duration";
49
- const strKillDurationSecondsHeader = "kill-duration-seconds";
50
- const strTimeDeltaHeader = "time-delta-millis";
44
+ interface IRequestUrlDetails {
45
+ url: string,
46
+ hdrs: { [key: string]: string },
47
+ useHdrs: boolean
48
+ }
51
49
 
52
50
  /**
53
51
  * Identifies the default notification reason to the action names
@@ -60,6 +58,24 @@ const _eventActionMap: any = {
60
58
  [EventBatchNotificationReason.SizeLimitExceeded]: strDropped
61
59
  };
62
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(strMsaDeviceTicket, strMsaDeviceTicket, false);
72
+ _addCollectorHeaderQsMapping(strClientVersion, strClientVersion);
73
+ _addCollectorHeaderQsMapping(strClientId, "Client-Id");
74
+ _addCollectorHeaderQsMapping(strApiKey, strApiKey);
75
+ _addCollectorHeaderQsMapping(strTimeDeltaToApply, strTimeDeltaToApply);
76
+ _addCollectorHeaderQsMapping(strUploadTime, strUploadTime);
77
+ _addCollectorHeaderQsMapping(strAuthXToken, strAuthXToken);
78
+
63
79
  type OnCompleteCallback = (status: number, headers: { [headerName: string]: string }, response?: string) => void;
64
80
 
65
81
  function _getResponseText(xhr: XMLHttpRequest | IXDomainRequest) {
@@ -102,10 +118,21 @@ function _hasHeader(headers: any, header: string) {
102
118
  return hasHeader;
103
119
  }
104
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
+
105
132
  /**
106
133
  * Class managing the sending of requests.
107
134
  */
108
- export default class HttpManager {
135
+ export class HttpManager {
109
136
  public sendHook: PayloadPreprocessorFunction | undefined;
110
137
  public sendListener: PayloadListenerFunction | undefined;
111
138
  public _responseHandlers: Array<(responseText: string) => void> = [];
@@ -114,9 +141,8 @@ export default class HttpManager {
114
141
  * @constructor
115
142
  * @param requestQueue - The queue that contains the requests to be sent.
116
143
  */
117
- constructor(maxEventsPerBatch: number, maxConnections: number, maxRetries: number, actions: BatchNotificationActions) {
118
- let _urlString: string = "?cors=true&" + strContentTypeHeader.toLowerCase() + "=" + defaultContentType + "&client-id=NO_AUTH&client-version="
119
- + FullVersionString;
144
+ constructor(maxEventsPerBatch: number, maxConnections: number, maxRequestRetriesBeforeBackoff: number, actions: BatchNotificationActions) {
145
+ let _urlString: string = "?cors=true&" + strContentTypeHeader.toLowerCase() + "=" + defaultContentType;
120
146
  let _killSwitch: EVTKillSwitch = new EVTKillSwitch();
121
147
  let _paused = false;
122
148
  let _clockSkewManager = new EVTClockSkewManager();
@@ -133,6 +159,9 @@ export default class HttpManager {
133
159
  let _enableEventTimings = false;
134
160
  let _cookieMgr: ICookieMgr;
135
161
  let _isUnloading = false;
162
+ let _useHeaders = false;
163
+ let _xhrTimeout: number;
164
+ let _disableXhrSync: boolean;
136
165
 
137
166
  dynamicProto(HttpManager, this, (_self) => {
138
167
  let _sendCredentials = true;
@@ -149,6 +178,8 @@ export default class HttpManager {
149
178
  }
150
179
 
151
180
  _urlString = endpointUrl + _urlString;
181
+
182
+ _useHeaders = !isUndefined(channelConfig.avoidOptions) ? !channelConfig.avoidOptions : true;
152
183
  _core = core;
153
184
  _cookieMgr = core.getCookieMgr();
154
185
  _enableEventTimings = !(_core.config as IExtendedConfiguration).disableEventTimings;
@@ -160,17 +191,15 @@ export default class HttpManager {
160
191
  enableCompoundKey = !!channelConfig.enableCompoundKey;
161
192
  }
162
193
 
163
- // For this to work the collection MUST return "Cache-control" in the OPTIONS response in the Allow-Control-Allow-Headers
164
- // ONLY un-comment the below once the collector has changed and deployed.
165
- // if (!!channelConfig.disableCacheHeader) {
166
- // // Stop Chrome from stalling/throttling requests see task #7178858
167
- // _self.addHeader("Cache-control", "no-cache, no-store");
168
- // }
194
+ _xhrTimeout = channelConfig.xhrTimeout;
195
+ _disableXhrSync = channelConfig.disableXhrSync;
169
196
 
170
197
  _useBeacons = !isReactNative(); // Only use beacons if not running in React Native
171
198
  _serializer = new Serializer(_core, valueSanitizer, stringifyObjects, enableCompoundKey);
172
199
 
173
200
  let syncHttpInterface = httpInterface;
201
+ let beaconHttpInterface: IXHROverride = channelConfig.alwaysUseXhrOverride ? httpInterface : null;
202
+ let fetchSyncHttpInterface: IXHROverride = channelConfig.alwaysUseXhrOverride ? httpInterface : null;
174
203
 
175
204
  if (!httpInterface) {
176
205
  _customHttpInterface = false;
@@ -210,8 +239,8 @@ export default class HttpManager {
210
239
  _sendInterfaces = {
211
240
  [EventSendType.Batched]: httpInterface,
212
241
  [EventSendType.Synchronous]: syncHttpInterface || _getSenderInterface([TransportType.Xhr, TransportType.Fetch, TransportType.Beacon], true),
213
- [EventSendType.SendBeacon]: _getSenderInterface([TransportType.Beacon, TransportType.Fetch, TransportType.Xhr], true) || syncHttpInterface,
214
- [EventSendType.SyncFetch]: _getSenderInterface([TransportType.Fetch, TransportType.Beacon, TransportType.Xhr], true) || syncHttpInterface
242
+ [EventSendType.SendBeacon]: beaconHttpInterface || _getSenderInterface([TransportType.Beacon, TransportType.Fetch], true) || syncHttpInterface || _getSenderInterface([TransportType.Xhr], true),
243
+ [EventSendType.SyncFetch]: fetchSyncHttpInterface || _getSenderInterface([TransportType.Fetch, TransportType.Beacon], true) || syncHttpInterface || _getSenderInterface([TransportType.Xhr], true)
215
244
  };
216
245
  };
217
246
 
@@ -256,6 +285,9 @@ export default class HttpManager {
256
285
  // It doesn't support custom headers, so no action is taken with current requestHeaders
257
286
  let xdr = new XDomainRequest();
258
287
  xdr.open(Method, payload.urlString);
288
+ if (payload.timeout) {
289
+ xdr.timeout = payload.timeout;
290
+ }
259
291
 
260
292
  // can't get the status code in xdr.
261
293
  xdr.onload = () => {
@@ -288,6 +320,9 @@ export default class HttpManager {
288
320
  }
289
321
 
290
322
  function _fetchSendPost(payload: IPayloadData, oncomplete: OnCompleteCallback, sync?: boolean) {
323
+ let theUrl = payload.urlString;
324
+ let ignoreResponse = false;
325
+ let responseHandled = false;
291
326
  let requestInit: RequestInit = {
292
327
  body: payload.data,
293
328
  method: Method,
@@ -296,6 +331,12 @@ export default class HttpManager {
296
331
 
297
332
  if (sync) {
298
333
  requestInit.keepalive = true;
334
+ if ((payload as IInternalPayloadData)._sendReason === SendRequestReason.Unload) {
335
+ // As a sync request (during unload), it is unlikely that we will get a chance to process the response so
336
+ // just like beacon send assume that the events have been accepted and processed
337
+ ignoreResponse = true;
338
+ theUrl += _noResponseQs;
339
+ }
299
340
  }
300
341
 
301
342
  if (_sendCredentials) {
@@ -308,7 +349,7 @@ export default class HttpManager {
308
349
  requestInit.headers = payload.headers;
309
350
  }
310
351
 
311
- fetch(payload.urlString, requestInit).then((response) => {
352
+ fetch(theUrl, requestInit).then((response) => {
312
353
  let headerMap = {};
313
354
  let responseText = "";
314
355
  if (response.headers) {
@@ -321,16 +362,41 @@ export default class HttpManager {
321
362
  responseText = text;
322
363
  });
323
364
  }
324
- _doOnComplete(oncomplete, response.status, headerMap, responseText);
325
- _handleCollectorResponse(responseText);
365
+
366
+ if (!responseHandled) {
367
+ responseHandled = true;
368
+ _doOnComplete(oncomplete, response.status, headerMap, responseText);
369
+ _handleCollectorResponse(responseText);
370
+ }
326
371
  }).catch((error) => {
327
372
  // In case there is an error in the request. Set the status to 0
328
373
  // so that the events can be retried later.
329
- _doOnComplete(oncomplete, 0, {});
374
+ if (!responseHandled) {
375
+ responseHandled = true;
376
+ _doOnComplete(oncomplete, 0, {});
377
+ }
330
378
  });
379
+
380
+ if (ignoreResponse && !responseHandled) {
381
+ // Assume success during unload processing
382
+ responseHandled = true;
383
+ _doOnComplete(oncomplete, 200, {});
384
+ }
385
+
386
+ if (!responseHandled && payload.timeout > 0) {
387
+ // Simulate timeout
388
+ _postManager._setTimeoutOverride(() => {
389
+ if (!responseHandled) {
390
+ // Assume a 500 response (which will cause a retry)
391
+ responseHandled = true;
392
+ _doOnComplete(oncomplete, 500, {});
393
+ }
394
+ }, payload.timeout);
395
+ }
331
396
  }
332
397
 
333
398
  function _xhrSendPost(payload: IPayloadData, oncomplete: OnCompleteCallback, sync?: boolean) {
399
+ let theUrl = payload.urlString;
334
400
 
335
401
  function _appendHeader(theHeaders, xhr, name) {
336
402
  if (!theHeaders[name] && xhr && xhr.getResponseHeader) {
@@ -363,33 +429,37 @@ export default class HttpManager {
363
429
  function xhrComplete(xhr, responseTxt?) {
364
430
  _doOnComplete(oncomplete, xhr.status, _getAllResponseHeaders(xhr), responseTxt);
365
431
  }
432
+ if (sync && payload.disableXhrSync) {
433
+ sync = false;
434
+ }
366
435
 
367
- let xhRequest = openXhr(Method, payload.urlString, _sendCredentials, true, sync);
436
+ let xhrRequest = openXhr(Method, theUrl, _sendCredentials, true, sync, payload.timeout);
368
437
 
369
438
  // Set custom headers (e.g. gzip) here (after open())
370
439
  objForEachKey(payload.headers, (name, value) => {
371
- xhRequest.setRequestHeader(name, value);
440
+ xhrRequest.setRequestHeader(name, value);
372
441
  });
373
- xhRequest.onload = () => {
374
- let response = _getResponseText(xhRequest);
375
- xhrComplete(xhRequest, response);
442
+ xhrRequest.onload = () => {
443
+ let response = _getResponseText(xhrRequest);
444
+ xhrComplete(xhrRequest, response);
376
445
  _handleCollectorResponse(response);
377
446
  };
378
- xhRequest.onerror = () => {
379
- xhrComplete(xhRequest);
447
+ xhrRequest.onerror = () => {
448
+ xhrComplete(xhrRequest);
380
449
  };
381
- xhRequest.ontimeout = () => {
382
- xhrComplete(xhRequest);
450
+ xhrRequest.ontimeout = () => {
451
+ xhrComplete(xhrRequest);
383
452
  };
384
- xhRequest.send(payload.data);
453
+ xhrRequest.send(payload.data);
385
454
  }
386
455
 
387
456
  function _doOnComplete(oncomplete: OnCompleteCallback, status: number, headers: { [headerName: string]: string }, response?: string) {
388
457
  try {
389
458
  oncomplete(status, headers, response);
390
459
  } catch (e) {
391
- _postManager.diagLog().throwInternal(LoggingSeverity.WARNING,
392
- _ExtendedInternalMessageId.SendPostOnCompleteFailure, dumpObj(e));
460
+ _throwInternal(_postManager.diagLog(),
461
+ eLoggingSeverity.WARNING,
462
+ _eExtendedInternalMessageId.SendPostOnCompleteFailure, dumpObj(e));
393
463
  }
394
464
  }
395
465
 
@@ -398,9 +468,11 @@ export default class HttpManager {
398
468
  let internalPayloadData = payload as IInternalPayloadData;
399
469
  let status = 200;
400
470
  let thePayload = internalPayloadData._thePayload;
471
+ let theUrl = payload.urlString + _noResponseQs;
472
+
401
473
  try {
402
474
  let nav = getNavigator();
403
- if (!nav.sendBeacon(payload.urlString, payload.data)) {
475
+ if (!nav.sendBeacon(theUrl, payload.data)) {
404
476
  if (thePayload) {
405
477
  // Failed to send entire payload so try and split data and try to send as much events as possible
406
478
  let droppedBatches: EventBatch[] = [];
@@ -408,7 +480,7 @@ export default class HttpManager {
408
480
  if (droppedBatches && theBatch && theBatch.count() > 0) {
409
481
  let theEvents = theBatch.events();
410
482
  for (let lp = 0; lp < theEvents.length; lp++) {
411
- if (!nav.sendBeacon(payload.urlString, _serializer.getEventBlob(theEvents[lp]))) {
483
+ if (!nav.sendBeacon(theUrl, _serializer.getEventBlob(theEvents[lp]))) {
412
484
  // Can't send anymore, so split the batch and drop the rest
413
485
  droppedBatches.push(theBatch.split(lp));
414
486
  break;
@@ -427,7 +499,7 @@ export default class HttpManager {
427
499
  }
428
500
 
429
501
  } catch (ex) {
430
- _postManager.diagLog().warnToConsole("Failed to send telemetry using sendBeacon API. Ex:" + ex);
502
+ _postManager.diagLog().warnToConsole("Failed to send telemetry using sendBeacon API. Ex:" + dumpObj(ex));
431
503
  status = 0;
432
504
  } finally {
433
505
  _doOnComplete(oncomplete, status, {}, "");
@@ -665,13 +737,38 @@ export default class HttpManager {
665
737
  _sendBatchesNotification(droppedBatches, EventBatchNotificationReason.KillSwitch, sendType);
666
738
  }, () => ({ batches: _createDebugBatches(orgBatches), retryCount, isTeardown, isSynchronous, sendReason, useSendBeacon: _isBeaconPayload(sendType), sendType }), !isSynchronous);
667
739
  } catch (ex) {
668
- _postManager.diagLog().throwInternal(LoggingSeverity.WARNING,
669
- _ExtendedInternalMessageId.CannotSerializeObject, "Unexpected Exception sending batch: " + dumpObj(ex));
740
+ _throwInternal(_postManager.diagLog(),
741
+ eLoggingSeverity.WARNING,
742
+ _eInternalMessageId.CannotSerializeObject, "Unexpected Exception sending batch: " + dumpObj(ex));
670
743
  }
671
744
  }
672
745
 
673
- function _buildQueryString(thePayload: ISerializedPayload) {
674
- let urlString = _urlString;
746
+ function _buildRequestDetails(thePayload: ISerializedPayload, useHeaders: boolean): IRequestUrlDetails {
747
+ let requestDetails: IRequestUrlDetails = {
748
+ url: _urlString,
749
+ hdrs: {},
750
+ useHdrs: false // Assume no headers
751
+ };
752
+
753
+ if (!useHeaders) {
754
+ // Attempt to map headers to a query string if possible
755
+ objForEachKey(_headers, (name, value) => {
756
+ if (_collectorHeaderToQs[name]) {
757
+ _addRequestDetails(requestDetails, _collectorHeaderToQs[name], value, false);
758
+ } else {
759
+ // No mapping, so just include in the headers anyway (may not get sent if using sendBeacon())
760
+ requestDetails.hdrs[name] = value;
761
+ requestDetails.useHdrs = true;
762
+ }
763
+ });
764
+ } else {
765
+ // Copy the pre-defined headers into the payload headers
766
+ requestDetails.hdrs = extend(requestDetails.hdrs, _headers);
767
+ requestDetails.useHdrs = (objKeys(requestDetails.hdrs).length > 0);
768
+ }
769
+
770
+ _addRequestDetails(requestDetails, strClientId, "NO_AUTH", useHeaders);
771
+ _addRequestDetails(requestDetails, strClientVersion, FullVersionString, useHeaders);
675
772
 
676
773
  let apiQsKeys = "";
677
774
  arrForEach(thePayload.apiKeys, (apiKey) => {
@@ -682,33 +779,30 @@ export default class HttpManager {
682
779
  apiQsKeys += apiKey;
683
780
  });
684
781
 
685
- if (apiQsKeys.length > 0) {
686
- urlString += "&apikey=" + apiQsKeys;
687
- }
688
-
689
- urlString += "&upload-time=" + dateNow().toString();
782
+ _addRequestDetails(requestDetails, strApiKey, apiQsKeys, useHeaders);
783
+ _addRequestDetails(requestDetails, strUploadTime, dateNow().toString(), useHeaders);
690
784
 
691
785
  let msfpc = _getMsfpc(thePayload);
692
786
  if (isValueAssigned(msfpc)) {
693
- urlString = urlString + "&ext.intweb.msfpc=" + msfpc;
787
+ requestDetails.url += "&ext.intweb.msfpc=" + msfpc;
694
788
  }
695
789
 
696
790
  if (_clockSkewManager.shouldAddClockSkewHeaders()) {
697
- urlString += "&time-delta-to-apply-millis=" + _clockSkewManager.getClockSkewHeaderValue();
791
+ _addRequestDetails(requestDetails, strTimeDeltaToApply, _clockSkewManager.getClockSkewHeaderValue(), useHeaders);
698
792
  }
699
793
 
700
794
  if (_core.getWParam) {
701
795
  let wParam = _core.getWParam();
702
796
  if (wParam >= 0) {
703
- urlString += "&w=" + wParam;
797
+ requestDetails.url += "&w=" + wParam;
704
798
  }
705
799
  }
706
800
 
707
801
  for (let i = 0; i < _queryStringParameters.length; i++) {
708
- urlString += "&" + _queryStringParameters[i].name + "=" + _queryStringParameters[i].value;
802
+ requestDetails.url += "&" + _queryStringParameters[i].name + "=" + _queryStringParameters[i].value;
709
803
  }
710
804
 
711
- return urlString;
805
+ return requestDetails;
712
806
  }
713
807
 
714
808
  function _canUseSendBeaconApi() {
@@ -723,9 +817,25 @@ export default class HttpManager {
723
817
  function _doPayloadSend(thePayload: ISerializedPayload, serializationStart: number, serializationCompleted: number, sendReason: SendRequestReason) {
724
818
 
725
819
  if (thePayload && thePayload.payloadBlob && thePayload.payloadBlob.length > 0) {
726
- let urlString = _buildQueryString(thePayload);
820
+ let useSendHook = !!_self.sendHook;
821
+ let sendInterface = _sendInterfaces[thePayload.sendType];
822
+
823
+ // Send all data using a beacon style transport if closing mode is on or channel was teared down
824
+ if (!_isBeaconPayload(thePayload.sendType) && thePayload.isBeacon && thePayload.sendReason === SendRequestReason.Unload) {
825
+ sendInterface = _sendInterfaces[EventSendType.SendBeacon] || _sendInterfaces[EventSendType.SyncFetch] || sendInterface;
826
+ }
827
+
828
+ let useHeaders = _useHeaders;
829
+
830
+ // Disable header usage if we know we are using sendBeacon as additional headers are not supported
831
+ if (thePayload.isBeacon || sendInterface._transport === TransportType.Beacon) {
832
+ useHeaders = false;
833
+ }
834
+
835
+ let requestDetails = _buildRequestDetails(thePayload, useHeaders);
836
+ useHeaders = useHeaders || requestDetails.useHdrs;
837
+
727
838
  let sendEventStart = getTime();
728
- let strSendAttempt = "sendAttempt";
729
839
 
730
840
  doPerf(_core, () => "HttpManager:_doPayloadSend", () => {
731
841
  // Increment the send attempt count and add timings after packaging (So it's not serialized in the 1st attempt)
@@ -757,28 +867,29 @@ export default class HttpManager {
757
867
  // eslint-disable-next-line prefer-const
758
868
  let orgPayloadData: IInternalPayloadData = {
759
869
  data: thePayload.payloadBlob,
760
- urlString: urlString,
761
- headers: extend({}, _headers),
870
+ urlString: requestDetails.url,
871
+ headers: requestDetails.hdrs,
762
872
  _thePayload: thePayload,
763
- _sendReason: sendReason
873
+ _sendReason: sendReason,
874
+ timeout: _xhrTimeout
764
875
  };
765
876
 
766
- if (!_hasHeader(orgPayloadData.headers, strCacheControl)) {
767
- orgPayloadData.headers[strCacheControl] = defaultCacheControl;
877
+ if (!isUndefined(_disableXhrSync)) {
878
+ orgPayloadData.disableXhrSync = !!_disableXhrSync;
768
879
  }
769
880
 
770
- if (!_hasHeader(orgPayloadData.headers, strContentTypeHeader)) {
771
- orgPayloadData.headers[strContentTypeHeader] = defaultContentType;
881
+ // Only automatically add the following headers if already sending headers and we are not attempting to avoid an options call
882
+ if (useHeaders) {
883
+ if (!_hasHeader(orgPayloadData.headers, strCacheControl)) {
884
+ orgPayloadData.headers[strCacheControl] = defaultCacheControl;
885
+ }
886
+
887
+ if (!_hasHeader(orgPayloadData.headers, strContentTypeHeader)) {
888
+ orgPayloadData.headers[strContentTypeHeader] = defaultContentType;
889
+ }
772
890
  }
773
891
 
774
892
  let sender: (payload: IPayloadData) => void = null;
775
- let useSendHook = !!_self.sendHook;
776
-
777
- let sendInterface = _sendInterfaces[thePayload.sendType];
778
- // Send all data using a beacon style transport if closing mode is on or channel was teared down
779
- if (!_isBeaconPayload(thePayload.sendType) && thePayload.isBeacon && thePayload.sendReason === SendRequestReason.Unload) {
780
- sendInterface = _sendInterfaces[EventSendType.SendBeacon] || _sendInterfaces[EventSendType.SyncFetch] || sendInterface;
781
- }
782
893
 
783
894
  if (sendInterface) {
784
895
  // Send sync requests if the request is immediate or we are tearing down telemetry.
@@ -822,7 +933,9 @@ export default class HttpManager {
822
933
  let hookData: IPayloadData = {
823
934
  data: orgPayloadData.data,
824
935
  urlString: orgPayloadData.urlString,
825
- headers: extend({}, orgPayloadData.headers)
936
+ headers: extend({}, orgPayloadData.headers),
937
+ timeout: orgPayloadData.timeout,
938
+ disableXhrSync: orgPayloadData.disableXhrSync
826
939
  };
827
940
 
828
941
  let senderCalled = false;
@@ -908,13 +1021,13 @@ export default class HttpManager {
908
1021
 
909
1022
  // Disabling triple-equals rule to avoid httpOverrides from failing because they are returning a string value
910
1023
  // tslint:disable-next-line:triple-equals
911
- if (status == 200) {
1024
+ if (status == 200 || status == 204) {
912
1025
  // Response was successfully sent
913
1026
  reason = EventBatchNotificationReason.Complete;
914
1027
  return;
915
1028
  }
916
1029
 
917
- if (!RetryPolicy.shouldRetryForStatus(status) || thePayload.numEvents <= 0) {
1030
+ if (!retryPolicyShouldRetryForStatus(status) || thePayload.numEvents <= 0) {
918
1031
  // Only retry for specific response codes and if there is still events after kill switch processing
919
1032
  shouldRetry = false;
920
1033
  }
@@ -928,7 +1041,8 @@ export default class HttpManager {
928
1041
  reason = EventBatchNotificationReason.RequeueEvents;
929
1042
  let retryCount = thePayload.retryCnt;
930
1043
  if (thePayload.sendType === EventSendType.Batched) {
931
- if (retryCount < maxRetries) {
1044
+ // attempt to resend the entire batch
1045
+ if (retryCount < maxRequestRetriesBeforeBackoff) {
932
1046
  isRetrying = true;
933
1047
  _doAction(() => {
934
1048
  // try to resend the same batches
@@ -940,9 +1054,14 @@ export default class HttpManager {
940
1054
  }
941
1055
 
942
1056
  _sendBatches(thePayload.batches, retryCount + 1, thePayload.isTeardown, _isUnloading ? EventSendType.SendBeacon : thePayload.sendType, SendRequestReason.Retry);
943
- }, _isUnloading, RetryPolicy.getMillisToBackoffForRetry(retryCount));
1057
+ }, _isUnloading, retryPolicyGetMillisToBackoffForRetry(retryCount));
944
1058
  } else {
945
1059
  backOffTrans = true;
1060
+ if (_isUnloading) {
1061
+ // we are unloading so don't try and requeue the events otherwise let the events get requeued and resent during the backoff sending
1062
+ // This will also cause the events to be purged based on the priority (if necessary)
1063
+ reason = EventBatchNotificationReason.NonRetryableStatus;
1064
+ }
946
1065
  }
947
1066
  }
948
1067
  }
@@ -1048,12 +1167,9 @@ export default class HttpManager {
1048
1167
 
1049
1168
  function _getMsfpc(thePayload: ISerializedPayload): string {
1050
1169
  for (let lp = 0; lp < thePayload.batches.length; lp++) {
1051
- let batchEvents = thePayload.batches[lp].events();
1052
- for (let evtLp = 0; evtLp < batchEvents.length; evtLp++) {
1053
- let intWeb = ((batchEvents[evtLp].ext || {})["intweb"] || {});
1054
- if (isValueAssigned(intWeb["msfpc"])) {
1055
- return encodeURIComponent(intWeb["msfpc"]);
1056
- }
1170
+ let msfpc = thePayload.batches[lp].Msfpc();
1171
+ if (msfpc) {
1172
+ return encodeURIComponent(msfpc);
1057
1173
  }
1058
1174
  }
1059
1175
 
@@ -1067,16 +1183,17 @@ export default class HttpManager {
1067
1183
  try {
1068
1184
  responseHandlers[i](responseText);
1069
1185
  } catch (e) {
1070
- _postManager.diagLog().throwInternal(LoggingSeverity.CRITICAL,
1071
- _ExtendedInternalMessageId.PostResponseHandler,
1186
+ _throwInternal(_postManager.diagLog(),
1187
+ eLoggingSeverity.CRITICAL,
1188
+ _eExtendedInternalMessageId.PostResponseHandler,
1072
1189
  "Response handler failed: " + e);
1073
1190
  }
1074
1191
  }
1075
1192
  if (responseText) {
1076
1193
  let response = JSON.parse(responseText) as ICollectorResult;
1077
- if (isValueAssigned(response.webResult) && isValueAssigned(response.webResult.msfpc)) {
1194
+ if (isValueAssigned(response.webResult) && isValueAssigned(response.webResult[strMsfpc])) {
1078
1195
  // Set cookie
1079
- _cookieMgr.set("MSFPC", response.webResult.msfpc, 365 * 86400);
1196
+ _cookieMgr.set("MSFPC", response.webResult[strMsfpc], 365 * 86400);
1080
1197
  }
1081
1198
  }
1082
1199
  } catch (ex) {
@@ -1095,8 +1212,9 @@ export default class HttpManager {
1095
1212
  try {
1096
1213
  theAction.call(actions, theBatches, batchReason, isSyncRequest, sendType);
1097
1214
  } catch (e) {
1098
- _postManager.diagLog().throwInternal(LoggingSeverity.CRITICAL,
1099
- _ExtendedInternalMessageId.NotificationException,
1215
+ _throwInternal(_postManager.diagLog(),
1216
+ eLoggingSeverity.CRITICAL,
1217
+ _eInternalMessageId.NotificationException,
1100
1218
  "send request notification failed: " + e);
1101
1219
  }
1102
1220
  }, sendSync || isSyncRequest, 0);