@microsoft/1ds-post-js 3.1.8 → 3.1.11

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 (56) hide show
  1. package/README.md +124 -5
  2. package/bundle/{ms.post-3.1.8.gbl.js → ms.post-3.1.11.gbl.js} +353 -172
  3. package/bundle/ms.post-3.1.11.gbl.js.map +1 -0
  4. package/bundle/ms.post-3.1.11.gbl.min.js +7 -0
  5. package/bundle/ms.post-3.1.11.gbl.min.js.map +1 -0
  6. package/bundle/ms.post-3.1.11.integrity.json +46 -0
  7. package/bundle/{ms.post-3.1.8.js → ms.post-3.1.11.js} +353 -172
  8. package/bundle/ms.post-3.1.11.js.map +1 -0
  9. package/bundle/ms.post-3.1.11.min.js +7 -0
  10. package/bundle/ms.post-3.1.11.min.js.map +1 -0
  11. package/bundle/ms.post.gbl.js +352 -171
  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 +352 -171
  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 +238 -116
  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 +172 -85
  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.js +55 -31
  40. package/dist-esm/src/PostChannel.js.map +1 -1
  41. package/dist-esm/src/RetryPolicy.js +1 -1
  42. package/dist-esm/src/Serializer.js +1 -1
  43. package/dist-esm/src/typings/XDomainRequest.js +1 -1
  44. package/package.json +3 -3
  45. package/src/Constants.ts +28 -0
  46. package/src/DataModels.ts +68 -0
  47. package/src/EventBatch.ts +47 -14
  48. package/src/HttpManager.ts +206 -84
  49. package/src/PostChannel.ts +60 -31
  50. package/bundle/ms.post-3.1.8.gbl.js.map +0 -1
  51. package/bundle/ms.post-3.1.8.gbl.min.js +0 -7
  52. package/bundle/ms.post-3.1.8.gbl.min.js.map +0 -1
  53. package/bundle/ms.post-3.1.8.integrity.json +0 -46
  54. package/bundle/ms.post-3.1.8.js.map +0 -1
  55. package/bundle/ms.post-3.1.8.min.js +0 -7
  56. package/bundle/ms.post-3.1.8.min.js.map +0 -1
@@ -24,6 +24,16 @@ 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,27 @@ 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) {
65
+ _collectorQsHeaders[qsName] = headerName;
66
+ _collectorHeaderToQs[headerName] = qsName;
67
+ }
68
+
69
+ _addCollectorHeaderQsMapping(strMsaDeviceTicket, strMsaDeviceTicket);
70
+
71
+ // ----------------------------------------------------------------------------------------------------------------
72
+ // Task 12886642: Need to wait until an updated version of the collector is released to return these as allowed in the OPTIONS call
73
+ // ----------------------------------------------------------------------------------------------------------------
74
+ //_addCollectorHeaderQsMapping(strClientVersion, strClientVersion);
75
+ //_addCollectorHeaderQsMapping(strClientId, "Client-Id");
76
+ //_addCollectorHeaderQsMapping(strApiKey, strApiKey);
77
+ //_addCollectorHeaderQsMapping(strTimeDeltaToApply, strTimeDeltaToApply);
78
+ //_addCollectorHeaderQsMapping(strUploadTime, strUploadTime);
79
+ //_addCollectorHeaderQsMapping(strAuthXToken, strAuthXToken);
80
+ // ----------------------------------------------------------------------------------------------------------------
81
+
63
82
  type OnCompleteCallback = (status: number, headers: { [headerName: string]: string }, response?: string) => void;
64
83
 
65
84
  function _getResponseText(xhr: XMLHttpRequest | IXDomainRequest) {
@@ -102,10 +121,21 @@ function _hasHeader(headers: any, header: string) {
102
121
  return hasHeader;
103
122
  }
104
123
 
124
+ function _addRequestDetails(details: IRequestUrlDetails, name: string, value: string, useHeaders: boolean) {
125
+ if (name && value && value.length > 0) {
126
+ if (useHeaders && _collectorQsHeaders[name]) {
127
+ details.hdrs[_collectorQsHeaders[name]] = value;
128
+ details.useHdrs = true;
129
+ } else {
130
+ details.url += "&" + name + "=" + value;
131
+ }
132
+ }
133
+ }
134
+
105
135
  /**
106
136
  * Class managing the sending of requests.
107
137
  */
108
- export default class HttpManager {
138
+ export class HttpManager {
109
139
  public sendHook: PayloadPreprocessorFunction | undefined;
110
140
  public sendListener: PayloadListenerFunction | undefined;
111
141
  public _responseHandlers: Array<(responseText: string) => void> = [];
@@ -114,9 +144,8 @@ export default class HttpManager {
114
144
  * @constructor
115
145
  * @param requestQueue - The queue that contains the requests to be sent.
116
146
  */
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;
147
+ constructor(maxEventsPerBatch: number, maxConnections: number, maxRequestRetriesBeforeBackoff: number, actions: BatchNotificationActions) {
148
+ let _urlString: string = "?cors=true&" + strContentTypeHeader.toLowerCase() + "=" + defaultContentType;
120
149
  let _killSwitch: EVTKillSwitch = new EVTKillSwitch();
121
150
  let _paused = false;
122
151
  let _clockSkewManager = new EVTClockSkewManager();
@@ -133,6 +162,9 @@ export default class HttpManager {
133
162
  let _enableEventTimings = false;
134
163
  let _cookieMgr: ICookieMgr;
135
164
  let _isUnloading = false;
165
+ let _useHeaders = false;
166
+ let _xhrTimeout: number;
167
+ let _disableXhrSync: boolean;
136
168
 
137
169
  dynamicProto(HttpManager, this, (_self) => {
138
170
  let _sendCredentials = true;
@@ -149,6 +181,9 @@ export default class HttpManager {
149
181
  }
150
182
 
151
183
  _urlString = endpointUrl + _urlString;
184
+
185
+ // Task 12886642: Defaulting to 'false' until the Collector handles sending upload-time header in the OPTIONS call
186
+ _useHeaders = !isUndefined(channelConfig.avoidOptions) ? !channelConfig.avoidOptions : false;
152
187
  _core = core;
153
188
  _cookieMgr = core.getCookieMgr();
154
189
  _enableEventTimings = !(_core.config as IExtendedConfiguration).disableEventTimings;
@@ -160,17 +195,15 @@ export default class HttpManager {
160
195
  enableCompoundKey = !!channelConfig.enableCompoundKey;
161
196
  }
162
197
 
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
- // }
198
+ _xhrTimeout = channelConfig.xhrTimeout;
199
+ _disableXhrSync = channelConfig.disableXhrSync;
169
200
 
170
201
  _useBeacons = !isReactNative(); // Only use beacons if not running in React Native
171
202
  _serializer = new Serializer(_core, valueSanitizer, stringifyObjects, enableCompoundKey);
172
203
 
173
204
  let syncHttpInterface = httpInterface;
205
+ let beaconHttpInterface: IXHROverride = channelConfig.alwaysUseXhrOverride ? httpInterface : null;
206
+ let fetchSyncHttpInterface: IXHROverride = channelConfig.alwaysUseXhrOverride ? httpInterface : null;
174
207
 
175
208
  if (!httpInterface) {
176
209
  _customHttpInterface = false;
@@ -210,8 +243,8 @@ export default class HttpManager {
210
243
  _sendInterfaces = {
211
244
  [EventSendType.Batched]: httpInterface,
212
245
  [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
246
+ [EventSendType.SendBeacon]: beaconHttpInterface || _getSenderInterface([TransportType.Beacon, TransportType.Fetch], true) || syncHttpInterface || _getSenderInterface([TransportType.Xhr], true),
247
+ [EventSendType.SyncFetch]: fetchSyncHttpInterface || _getSenderInterface([TransportType.Fetch, TransportType.Beacon], true) || syncHttpInterface || _getSenderInterface([TransportType.Xhr], true)
215
248
  };
216
249
  };
217
250
 
@@ -256,6 +289,9 @@ export default class HttpManager {
256
289
  // It doesn't support custom headers, so no action is taken with current requestHeaders
257
290
  let xdr = new XDomainRequest();
258
291
  xdr.open(Method, payload.urlString);
292
+ if (payload.timeout) {
293
+ xdr.timeout = payload.timeout;
294
+ }
259
295
 
260
296
  // can't get the status code in xdr.
261
297
  xdr.onload = () => {
@@ -288,6 +324,9 @@ export default class HttpManager {
288
324
  }
289
325
 
290
326
  function _fetchSendPost(payload: IPayloadData, oncomplete: OnCompleteCallback, sync?: boolean) {
327
+ let theUrl = payload.urlString;
328
+ let ignoreResponse = false;
329
+ let responseHandled = false;
291
330
  let requestInit: RequestInit = {
292
331
  body: payload.data,
293
332
  method: Method,
@@ -296,6 +335,12 @@ export default class HttpManager {
296
335
 
297
336
  if (sync) {
298
337
  requestInit.keepalive = true;
338
+ if ((payload as IInternalPayloadData)._sendReason === SendRequestReason.Unload) {
339
+ // As a sync request (during unload), it is unlikely that we will get a chance to process the response so
340
+ // just like beacon send assume that the events have been accepted and processed
341
+ ignoreResponse = true;
342
+ theUrl += _noResponseQs;
343
+ }
299
344
  }
300
345
 
301
346
  if (_sendCredentials) {
@@ -308,7 +353,7 @@ export default class HttpManager {
308
353
  requestInit.headers = payload.headers;
309
354
  }
310
355
 
311
- fetch(payload.urlString, requestInit).then((response) => {
356
+ fetch(theUrl, requestInit).then((response) => {
312
357
  let headerMap = {};
313
358
  let responseText = "";
314
359
  if (response.headers) {
@@ -321,16 +366,41 @@ export default class HttpManager {
321
366
  responseText = text;
322
367
  });
323
368
  }
324
- _doOnComplete(oncomplete, response.status, headerMap, responseText);
325
- _handleCollectorResponse(responseText);
369
+
370
+ if (!responseHandled) {
371
+ responseHandled = true;
372
+ _doOnComplete(oncomplete, response.status, headerMap, responseText);
373
+ _handleCollectorResponse(responseText);
374
+ }
326
375
  }).catch((error) => {
327
376
  // In case there is an error in the request. Set the status to 0
328
377
  // so that the events can be retried later.
329
- _doOnComplete(oncomplete, 0, {});
378
+ if (!responseHandled) {
379
+ responseHandled = true;
380
+ _doOnComplete(oncomplete, 0, {});
381
+ }
330
382
  });
383
+
384
+ if (ignoreResponse && !responseHandled) {
385
+ // Assume success during unload processing
386
+ responseHandled = true;
387
+ _doOnComplete(oncomplete, 200, {});
388
+ }
389
+
390
+ if (!responseHandled && payload.timeout > 0) {
391
+ // Simulate timeout
392
+ _postManager._setTimeoutOverride(() => {
393
+ if (!responseHandled) {
394
+ // Assume a 500 response (which will cause a retry)
395
+ responseHandled = true;
396
+ _doOnComplete(oncomplete, 500, {});
397
+ }
398
+ }, payload.timeout);
399
+ }
331
400
  }
332
401
 
333
402
  function _xhrSendPost(payload: IPayloadData, oncomplete: OnCompleteCallback, sync?: boolean) {
403
+ let theUrl = payload.urlString;
334
404
 
335
405
  function _appendHeader(theHeaders, xhr, name) {
336
406
  if (!theHeaders[name] && xhr && xhr.getResponseHeader) {
@@ -363,25 +433,28 @@ export default class HttpManager {
363
433
  function xhrComplete(xhr, responseTxt?) {
364
434
  _doOnComplete(oncomplete, xhr.status, _getAllResponseHeaders(xhr), responseTxt);
365
435
  }
436
+ if (sync && payload.disableXhrSync) {
437
+ sync = false;
438
+ }
366
439
 
367
- let xhRequest = openXhr(Method, payload.urlString, _sendCredentials, true, sync);
440
+ let xhrRequest = openXhr(Method, theUrl, _sendCredentials, true, sync, payload.timeout);
368
441
 
369
442
  // Set custom headers (e.g. gzip) here (after open())
370
443
  objForEachKey(payload.headers, (name, value) => {
371
- xhRequest.setRequestHeader(name, value);
444
+ xhrRequest.setRequestHeader(name, value);
372
445
  });
373
- xhRequest.onload = () => {
374
- let response = _getResponseText(xhRequest);
375
- xhrComplete(xhRequest, response);
446
+ xhrRequest.onload = () => {
447
+ let response = _getResponseText(xhrRequest);
448
+ xhrComplete(xhrRequest, response);
376
449
  _handleCollectorResponse(response);
377
450
  };
378
- xhRequest.onerror = () => {
379
- xhrComplete(xhRequest);
451
+ xhrRequest.onerror = () => {
452
+ xhrComplete(xhrRequest);
380
453
  };
381
- xhRequest.ontimeout = () => {
382
- xhrComplete(xhRequest);
454
+ xhrRequest.ontimeout = () => {
455
+ xhrComplete(xhrRequest);
383
456
  };
384
- xhRequest.send(payload.data);
457
+ xhrRequest.send(payload.data);
385
458
  }
386
459
 
387
460
  function _doOnComplete(oncomplete: OnCompleteCallback, status: number, headers: { [headerName: string]: string }, response?: string) {
@@ -398,9 +471,11 @@ export default class HttpManager {
398
471
  let internalPayloadData = payload as IInternalPayloadData;
399
472
  let status = 200;
400
473
  let thePayload = internalPayloadData._thePayload;
474
+ let theUrl = payload.urlString + _noResponseQs;
475
+
401
476
  try {
402
477
  let nav = getNavigator();
403
- if (!nav.sendBeacon(payload.urlString, payload.data)) {
478
+ if (!nav.sendBeacon(theUrl, payload.data)) {
404
479
  if (thePayload) {
405
480
  // Failed to send entire payload so try and split data and try to send as much events as possible
406
481
  let droppedBatches: EventBatch[] = [];
@@ -408,7 +483,7 @@ export default class HttpManager {
408
483
  if (droppedBatches && theBatch && theBatch.count() > 0) {
409
484
  let theEvents = theBatch.events();
410
485
  for (let lp = 0; lp < theEvents.length; lp++) {
411
- if (!nav.sendBeacon(payload.urlString, _serializer.getEventBlob(theEvents[lp]))) {
486
+ if (!nav.sendBeacon(theUrl, _serializer.getEventBlob(theEvents[lp]))) {
412
487
  // Can't send anymore, so split the batch and drop the rest
413
488
  droppedBatches.push(theBatch.split(lp));
414
489
  break;
@@ -670,8 +745,36 @@ export default class HttpManager {
670
745
  }
671
746
  }
672
747
 
673
- function _buildQueryString(thePayload: ISerializedPayload) {
674
- let urlString = _urlString;
748
+ function _buildRequestDetails(thePayload: ISerializedPayload, useHeaders: boolean): IRequestUrlDetails {
749
+ let requestDetails: IRequestUrlDetails = {
750
+ url: _urlString,
751
+ hdrs: {},
752
+ useHdrs: false // Assume no headers
753
+ };
754
+
755
+ // ----------------------------------------------------------------------------------------------------------------
756
+ // Task 12886642: Need to wait until an updated version of the collector is released to return these as allowed in the OPTIONS call
757
+ // ----------------------------------------------------------------------------------------------------------------
758
+ // if (!useHeaders) {
759
+ // // Attempt to map headers to a query string if possible
760
+ // objForEachKey(_headers, (name, value) => {
761
+ // if (_collectorHeaderToQs[name]) {
762
+ // _addRequestDetails(requestDetails, _collectorHeaderToQs[name], value, false);
763
+ // } else {
764
+ // // No mapping, so just include in the headers anyway (may not get sent if using sendBeacon())
765
+ // requestDetails.hdrs[name] = value;
766
+ // requestDetails.useHdrs = true;
767
+ // }
768
+ // });
769
+ // } else {
770
+ // Copy the pre-defined headers into the payload headers
771
+ requestDetails.hdrs = extend(requestDetails.hdrs, _headers);
772
+ requestDetails.useHdrs = (objKeys(requestDetails.hdrs).length > 0);
773
+ // }
774
+ // ----------------------------------------------------------------------------------------------------------------
775
+
776
+ _addRequestDetails(requestDetails, strClientId, "NO_AUTH", useHeaders);
777
+ _addRequestDetails(requestDetails, strClientVersion, FullVersionString, useHeaders);
675
778
 
676
779
  let apiQsKeys = "";
677
780
  arrForEach(thePayload.apiKeys, (apiKey) => {
@@ -682,33 +785,30 @@ export default class HttpManager {
682
785
  apiQsKeys += apiKey;
683
786
  });
684
787
 
685
- if (apiQsKeys.length > 0) {
686
- urlString += "&apikey=" + apiQsKeys;
687
- }
688
-
689
- urlString += "&upload-time=" + dateNow().toString();
788
+ _addRequestDetails(requestDetails, strApiKey, apiQsKeys, useHeaders);
789
+ _addRequestDetails(requestDetails, strUploadTime, dateNow().toString(), useHeaders);
690
790
 
691
791
  let msfpc = _getMsfpc(thePayload);
692
792
  if (isValueAssigned(msfpc)) {
693
- urlString = urlString + "&ext.intweb.msfpc=" + msfpc;
793
+ requestDetails.url += "&ext.intweb.msfpc=" + msfpc;
694
794
  }
695
795
 
696
796
  if (_clockSkewManager.shouldAddClockSkewHeaders()) {
697
- urlString += "&time-delta-to-apply-millis=" + _clockSkewManager.getClockSkewHeaderValue();
797
+ _addRequestDetails(requestDetails, strTimeDeltaToApply, _clockSkewManager.getClockSkewHeaderValue(), useHeaders);
698
798
  }
699
799
 
700
800
  if (_core.getWParam) {
701
801
  let wParam = _core.getWParam();
702
802
  if (wParam >= 0) {
703
- urlString += "&w=" + wParam;
803
+ requestDetails.url += "&w=" + wParam;
704
804
  }
705
805
  }
706
806
 
707
807
  for (let i = 0; i < _queryStringParameters.length; i++) {
708
- urlString += "&" + _queryStringParameters[i].name + "=" + _queryStringParameters[i].value;
808
+ requestDetails.url += "&" + _queryStringParameters[i].name + "=" + _queryStringParameters[i].value;
709
809
  }
710
810
 
711
- return urlString;
811
+ return requestDetails;
712
812
  }
713
813
 
714
814
  function _canUseSendBeaconApi() {
@@ -723,9 +823,25 @@ export default class HttpManager {
723
823
  function _doPayloadSend(thePayload: ISerializedPayload, serializationStart: number, serializationCompleted: number, sendReason: SendRequestReason) {
724
824
 
725
825
  if (thePayload && thePayload.payloadBlob && thePayload.payloadBlob.length > 0) {
726
- let urlString = _buildQueryString(thePayload);
826
+ let useSendHook = !!_self.sendHook;
827
+ let sendInterface = _sendInterfaces[thePayload.sendType];
828
+
829
+ // Send all data using a beacon style transport if closing mode is on or channel was teared down
830
+ if (!_isBeaconPayload(thePayload.sendType) && thePayload.isBeacon && thePayload.sendReason === SendRequestReason.Unload) {
831
+ sendInterface = _sendInterfaces[EventSendType.SendBeacon] || _sendInterfaces[EventSendType.SyncFetch] || sendInterface;
832
+ }
833
+
834
+ let useHeaders = _useHeaders;
835
+
836
+ // Disable header usage if we know we are using sendBeacon as additional headers are not supported
837
+ if (thePayload.isBeacon || sendInterface._transport === TransportType.Beacon) {
838
+ useHeaders = false;
839
+ }
840
+
841
+ let requestDetails = _buildRequestDetails(thePayload, useHeaders);
842
+ useHeaders = useHeaders || requestDetails.useHdrs;
843
+
727
844
  let sendEventStart = getTime();
728
- let strSendAttempt = "sendAttempt";
729
845
 
730
846
  doPerf(_core, () => "HttpManager:_doPayloadSend", () => {
731
847
  // Increment the send attempt count and add timings after packaging (So it's not serialized in the 1st attempt)
@@ -757,28 +873,29 @@ export default class HttpManager {
757
873
  // eslint-disable-next-line prefer-const
758
874
  let orgPayloadData: IInternalPayloadData = {
759
875
  data: thePayload.payloadBlob,
760
- urlString: urlString,
761
- headers: extend({}, _headers),
876
+ urlString: requestDetails.url,
877
+ headers: requestDetails.hdrs,
762
878
  _thePayload: thePayload,
763
- _sendReason: sendReason
879
+ _sendReason: sendReason,
880
+ timeout: _xhrTimeout
764
881
  };
765
882
 
766
- if (!_hasHeader(orgPayloadData.headers, strCacheControl)) {
767
- orgPayloadData.headers[strCacheControl] = defaultCacheControl;
883
+ if (!isUndefined(_disableXhrSync)) {
884
+ orgPayloadData.disableXhrSync = !!_disableXhrSync;
768
885
  }
769
886
 
770
- if (!_hasHeader(orgPayloadData.headers, strContentTypeHeader)) {
771
- orgPayloadData.headers[strContentTypeHeader] = defaultContentType;
887
+ // Only automatically add the following headers if already sending headers and we are not attempting to avoid an options call
888
+ if (useHeaders) {
889
+ if (!_hasHeader(orgPayloadData.headers, strCacheControl)) {
890
+ orgPayloadData.headers[strCacheControl] = defaultCacheControl;
891
+ }
892
+
893
+ if (!_hasHeader(orgPayloadData.headers, strContentTypeHeader)) {
894
+ orgPayloadData.headers[strContentTypeHeader] = defaultContentType;
895
+ }
772
896
  }
773
897
 
774
898
  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
899
 
783
900
  if (sendInterface) {
784
901
  // Send sync requests if the request is immediate or we are tearing down telemetry.
@@ -822,7 +939,9 @@ export default class HttpManager {
822
939
  let hookData: IPayloadData = {
823
940
  data: orgPayloadData.data,
824
941
  urlString: orgPayloadData.urlString,
825
- headers: extend({}, orgPayloadData.headers)
942
+ headers: extend({}, orgPayloadData.headers),
943
+ timeout: orgPayloadData.timeout,
944
+ disableXhrSync: orgPayloadData.disableXhrSync
826
945
  };
827
946
 
828
947
  let senderCalled = false;
@@ -908,7 +1027,7 @@ export default class HttpManager {
908
1027
 
909
1028
  // Disabling triple-equals rule to avoid httpOverrides from failing because they are returning a string value
910
1029
  // tslint:disable-next-line:triple-equals
911
- if (status == 200) {
1030
+ if (status == 200 || status == 204) {
912
1031
  // Response was successfully sent
913
1032
  reason = EventBatchNotificationReason.Complete;
914
1033
  return;
@@ -928,7 +1047,8 @@ export default class HttpManager {
928
1047
  reason = EventBatchNotificationReason.RequeueEvents;
929
1048
  let retryCount = thePayload.retryCnt;
930
1049
  if (thePayload.sendType === EventSendType.Batched) {
931
- if (retryCount < maxRetries) {
1050
+ // attempt to resend the entire batch
1051
+ if (retryCount < maxRequestRetriesBeforeBackoff) {
932
1052
  isRetrying = true;
933
1053
  _doAction(() => {
934
1054
  // try to resend the same batches
@@ -943,6 +1063,11 @@ export default class HttpManager {
943
1063
  }, _isUnloading, RetryPolicy.getMillisToBackoffForRetry(retryCount));
944
1064
  } else {
945
1065
  backOffTrans = true;
1066
+ if (_isUnloading) {
1067
+ // we are unloading so don't try and requeue the events otherwise let the events get requeued and resent during the backoff sending
1068
+ // This will also cause the events to be purged based on the priority (if necessary)
1069
+ reason = EventBatchNotificationReason.NonRetryableStatus;
1070
+ }
946
1071
  }
947
1072
  }
948
1073
  }
@@ -1048,12 +1173,9 @@ export default class HttpManager {
1048
1173
 
1049
1174
  function _getMsfpc(thePayload: ISerializedPayload): string {
1050
1175
  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
- }
1176
+ let msfpc = thePayload.batches[lp].Msfpc();
1177
+ if (msfpc) {
1178
+ return encodeURIComponent(msfpc);
1057
1179
  }
1058
1180
  }
1059
1181
 
@@ -1074,9 +1196,9 @@ export default class HttpManager {
1074
1196
  }
1075
1197
  if (responseText) {
1076
1198
  let response = JSON.parse(responseText) as ICollectorResult;
1077
- if (isValueAssigned(response.webResult) && isValueAssigned(response.webResult.msfpc)) {
1199
+ if (isValueAssigned(response.webResult) && isValueAssigned(response.webResult[strMsfpc])) {
1078
1200
  // Set cookie
1079
- _cookieMgr.set("MSFPC", response.webResult.msfpc, 365 * 86400);
1201
+ _cookieMgr.set("MSFPC", response.webResult[strMsfpc], 365 * 86400);
1080
1202
  }
1081
1203
  }
1082
1204
  } catch (ex) {