utils.js 3.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. Object.defineProperty(exports, '__esModule', { value: true });
  2. const utils = require('@sentry/utils');
  3. const debugBuild = require('../debug-build.js');
  4. const helpers = require('../helpers.js');
  5. let cachedFetchImpl = undefined;
  6. /**
  7. * A special usecase for incorrectly wrapped Fetch APIs in conjunction with ad-blockers.
  8. * Whenever someone wraps the Fetch API and returns the wrong promise chain,
  9. * this chain becomes orphaned and there is no possible way to capture it's rejections
  10. * other than allowing it bubble up to this very handler. eg.
  11. *
  12. * const f = window.fetch;
  13. * window.fetch = function () {
  14. * const p = f.apply(this, arguments);
  15. *
  16. * p.then(function() {
  17. * console.log('hi.');
  18. * });
  19. *
  20. * return p;
  21. * }
  22. *
  23. * `p.then(function () { ... })` is producing a completely separate promise chain,
  24. * however, what's returned is `p` - the result of original `fetch` call.
  25. *
  26. * This mean, that whenever we use the Fetch API to send our own requests, _and_
  27. * some ad-blocker blocks it, this orphaned chain will _always_ reject,
  28. * effectively causing another event to be captured.
  29. * This makes a whole process become an infinite loop, which we need to somehow
  30. * deal with, and break it in one way or another.
  31. *
  32. * To deal with this issue, we are making sure that we _always_ use the real
  33. * browser Fetch API, instead of relying on what `window.fetch` exposes.
  34. * The only downside to this would be missing our own requests as breadcrumbs,
  35. * but because we are already not doing this, it should be just fine.
  36. *
  37. * Possible failed fetch error messages per-browser:
  38. *
  39. * Chrome: Failed to fetch
  40. * Edge: Failed to Fetch
  41. * Firefox: NetworkError when attempting to fetch resource
  42. * Safari: resource blocked by content blocker
  43. */
  44. function getNativeFetchImplementation() {
  45. if (cachedFetchImpl) {
  46. return cachedFetchImpl;
  47. }
  48. /* eslint-disable @typescript-eslint/unbound-method */
  49. // Fast path to avoid DOM I/O
  50. if (utils.isNativeFetch(helpers.WINDOW.fetch)) {
  51. return (cachedFetchImpl = helpers.WINDOW.fetch.bind(helpers.WINDOW));
  52. }
  53. const document = helpers.WINDOW.document;
  54. let fetchImpl = helpers.WINDOW.fetch;
  55. // eslint-disable-next-line deprecation/deprecation
  56. if (document && typeof document.createElement === 'function') {
  57. try {
  58. const sandbox = document.createElement('iframe');
  59. sandbox.hidden = true;
  60. document.head.appendChild(sandbox);
  61. const contentWindow = sandbox.contentWindow;
  62. if (contentWindow && contentWindow.fetch) {
  63. fetchImpl = contentWindow.fetch;
  64. }
  65. document.head.removeChild(sandbox);
  66. } catch (e) {
  67. debugBuild.DEBUG_BUILD && utils.logger.warn('Could not create sandbox iframe for pure fetch check, bailing to window.fetch: ', e);
  68. }
  69. }
  70. return (cachedFetchImpl = fetchImpl.bind(helpers.WINDOW));
  71. /* eslint-enable @typescript-eslint/unbound-method */
  72. }
  73. /** Clears cached fetch impl */
  74. function clearCachedFetchImplementation() {
  75. cachedFetchImpl = undefined;
  76. }
  77. exports.clearCachedFetchImplementation = clearCachedFetchImplementation;
  78. exports.getNativeFetchImplementation = getNativeFetchImplementation;
  79. //# sourceMappingURL=utils.js.map