Skip to content

Commit

Permalink
enable compat with service workers and v3 extensions
Browse files Browse the repository at this point in the history
  • Loading branch information
waltjones committed Nov 29, 2022
1 parent 433f6e8 commit aa62a9d
Show file tree
Hide file tree
Showing 6 changed files with 350 additions and 168 deletions.
16 changes: 14 additions & 2 deletions src/apiUtility.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ function getTransportFromOptions(options, defaults, url) {
var path = defaults.path;
var search = defaults.search;
var timeout = options.timeout;
var transport = detectTransport(options)

var proxy = options.proxy;
if (options.endpoint) {
Expand All @@ -42,16 +43,26 @@ function getTransportFromOptions(options, defaults, url) {
port: port,
path: path,
search: search,
proxy: proxy
proxy: proxy,
transport: transport
};
}

function detectTransport(options) {
var gWindow = ((typeof window != 'undefined') && window) || ((typeof self != 'undefined') && self);
var transport = options.defaultTransport || 'xhr';
if (typeof gWindow.fetch === 'undefined') transport = 'xhr';
if (typeof gWindow.XMLHttpRequest === 'undefined') transport = 'fetch';
return transport;
}

function transportOptions(transport, method) {
var protocol = transport.protocol || 'https:';
var port = transport.port || (protocol === 'http:' ? 80 : protocol === 'https:' ? 443 : undefined);
var hostname = transport.hostname;
var path = transport.path;
var timeout = transport.timeout;
var transportAPI = transport.transport;
if (transport.search) {
path = path + transport.search;
}
Expand All @@ -67,7 +78,8 @@ function transportOptions(transport, method) {
hostname: hostname,
path: path,
port: port,
method: method
method: method,
transport: transportAPI
};
}

Expand Down
195 changes: 29 additions & 166 deletions src/browser/transport.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
/*global XDomainRequest*/

var _ = require('../utility');
var logger = require('./logger');
var makeFetchRequest = require('./transport/fetch');
var makeXhrRequest = require('./transport/xhr');

/*
* accessToken may be embedded in payload but that should not
Expand All @@ -13,6 +12,7 @@ var logger = require('./logger');
* path
* port
* method
* transport ('xhr' | 'fetch')
* }
*
* params is an object containing key/value pairs. These
Expand All @@ -32,7 +32,9 @@ Transport.prototype.get = function(accessToken, options, params, callback, reque

var method = 'GET';
var url = _.formatUrl(options);
_makeZoneRequest(accessToken, url, method, null, callback, requestFactory, options.timeout);
this._makeZoneRequest(
accessToken, url, method, null, callback, requestFactory, options.timeout, options.transport
);
}

Transport.prototype.post = function(accessToken, options, payload, callback, requestFactory) {
Expand All @@ -57,7 +59,9 @@ Transport.prototype.post = function(accessToken, options, payload, callback, req
var writeData = stringifyResult.value;
var method = 'POST';
var url = _.formatUrl(options);
_makeZoneRequest(accessToken, url, method, writeData, callback, requestFactory, options.timeout);
this._makeZoneRequest(
accessToken, url, method, writeData, callback, requestFactory, options.timeout, options.transport
);
}

Transport.prototype.postJsonPayload = function (accessToken, options, jsonPayload, callback, requestFactory) {
Expand All @@ -67,26 +71,42 @@ Transport.prototype.postJsonPayload = function (accessToken, options, jsonPayloa

var method = 'POST';
var url = _.formatUrl(options);
_makeZoneRequest(accessToken, url, method, jsonPayload, callback, requestFactory, options.timeout);
this._makeZoneRequest(
accessToken, url, method, jsonPayload, callback, requestFactory, options.timeout, options.transport
);
}


// Wraps _makeRequest and if Angular 2+ Zone.js is detected, changes scope
// so Angular change detection isn't triggered on each API call.
// This is the equivalent of runOutsideAngular().
//
function _makeZoneRequest() {
Transport.prototype._makeZoneRequest = function () {
var gWindow = ((typeof window != 'undefined') && window) || ((typeof self != 'undefined') && self);
var currentZone = gWindow && gWindow.Zone && gWindow.Zone.current;
var args = Array.prototype.slice.call(arguments);

if (currentZone && currentZone._name === 'angular') {
var rootZone = currentZone._parent;
rootZone.run(function () {
_makeRequest.apply(undefined, args);
this._makeRequest.apply(undefined, args);
});
} else {
_makeRequest.apply(undefined, args);
this._makeRequest.apply(undefined, args);
}
}

Transport.prototype._makeRequest = function (
accessToken, url, method, data, callback, requestFactory, timeout, transport
) {
if (typeof RollbarProxy !== 'undefined') {
return _proxyRequest(data, callback);
}

if (transport === 'fetch') {
makeFetchRequest(accessToken, url, method, data, callback, timeout)
} else {
makeXhrRequest(accessToken, url, method, data, callback, requestFactory, timeout)
}
}

Expand All @@ -102,161 +122,4 @@ function _proxyRequest(json, callback) {
);
}

function _makeRequest(accessToken, url, method, data, callback, requestFactory, timeout) {
if (typeof RollbarProxy !== 'undefined') {
return _proxyRequest(data, callback);
}

var request;
if (requestFactory) {
request = requestFactory();
} else {
request = _createXMLHTTPObject();
}
if (!request) {
// Give up, no way to send requests
return callback(new Error('No way to send a request'));
}
try {
try {
var onreadystatechange = function() {
try {
if (onreadystatechange && request.readyState === 4) {
onreadystatechange = undefined;

var parseResponse = _.jsonParse(request.responseText);
if (_isSuccess(request)) {
callback(parseResponse.error, parseResponse.value);
return;
} else if (_isNormalFailure(request)) {
if (request.status === 403) {
// likely caused by using a server access token
var message = parseResponse.value && parseResponse.value.message;
logger.error(message);
}
// return valid http status codes
callback(new Error(String(request.status)));
} else {
// IE will return a status 12000+ on some sort of connection failure,
// so we return a blank error
// http://msdn.microsoft.com/en-us/library/aa383770%28VS.85%29.aspx
var msg = 'XHR response had no status code (likely connection failure)';
callback(_newRetriableError(msg));
}
}
} catch (ex) {
//jquery source mentions firefox may error out while accessing the
//request members if there is a network error
//https://github.com/jquery/jquery/blob/a938d7b1282fc0e5c52502c225ae8f0cef219f0a/src/ajax/xhr.js#L111
var exc;
if (ex && ex.stack) {
exc = ex;
} else {
exc = new Error(ex);
}
callback(exc);
}
};

request.open(method, url, true);
if (request.setRequestHeader) {
request.setRequestHeader('Content-Type', 'application/json');
request.setRequestHeader('X-Rollbar-Access-Token', accessToken);
}

if(_.isFiniteNumber(timeout)) {
request.timeout = timeout;
}

request.onreadystatechange = onreadystatechange;
request.send(data);
} catch (e1) {
// Sending using the normal xmlhttprequest object didn't work, try XDomainRequest
if (typeof XDomainRequest !== 'undefined') {

// Assume we are in a really old browser which has a bunch of limitations:
// http://blogs.msdn.com/b/ieinternals/archive/2010/05/13/xdomainrequest-restrictions-limitations-and-workarounds.aspx

// Extreme paranoia: if we have XDomainRequest then we have a window, but just in case
if (!window || !window.location) {
return callback(new Error('No window available during request, unknown environment'));
}

// If the current page is http, try and send over http
if (window.location.href.substring(0, 5) === 'http:' && url.substring(0, 5) === 'https') {
url = 'http' + url.substring(5);
}

var xdomainrequest = new XDomainRequest();
xdomainrequest.onprogress = function() {};
xdomainrequest.ontimeout = function() {
var msg = 'Request timed out';
var code = 'ETIMEDOUT';
callback(_newRetriableError(msg, code));
};
xdomainrequest.onerror = function() {
callback(new Error('Error during request'));
};
xdomainrequest.onload = function() {
var parseResponse = _.jsonParse(xdomainrequest.responseText);
callback(parseResponse.error, parseResponse.value);
};
xdomainrequest.open(method, url, true);
xdomainrequest.send(data);
} else {
callback(new Error('Cannot find a method to transport a request'));
}
}
} catch (e2) {
callback(e2);
}
}

function _createXMLHTTPObject() {
/* global ActiveXObject:false */

var factories = [
function () {
return new XMLHttpRequest();
},
function () {
return new ActiveXObject('Msxml2.XMLHTTP');
},
function () {
return new ActiveXObject('Msxml3.XMLHTTP');
},
function () {
return new ActiveXObject('Microsoft.XMLHTTP');
}
];
var xmlhttp;
var i;
var numFactories = factories.length;
for (i = 0; i < numFactories; i++) {
/* eslint-disable no-empty */
try {
xmlhttp = factories[i]();
break;
} catch (e) {
// pass
}
/* eslint-enable no-empty */
}
return xmlhttp;
}

function _isSuccess(r) {
return r && r.status && r.status === 200;
}

function _isNormalFailure(r) {
return r && _.isType(r.status, 'number') && r.status >= 400 && r.status < 600;
}

function _newRetriableError(message, code) {
var err = new Error(message);
err.code = code || 'ENOTFOUND';
return err;
}

module.exports = Transport;
35 changes: 35 additions & 0 deletions src/browser/transport/fetch.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
var logger = require('../logger');
var _ = require('../../utility');

function makeFetchRequest(accessToken, url, method, data, callback, timeout) {
var controller;
var timeoutId;

if(_.isFiniteNumber(timeout)) {
controller = new AbortController();
timeoutId = setTimeout(() => controller.abort(), timeout);
}

fetch(url, {
method: method,
headers: {
'Content-Type': 'application/json',
'X-Rollbar-Access-Token': accessToken,
signal: controller.signal
},
body: data,
})
.then((response) => {
if (timeoutId) clearTimeout(timeoutId);
return response.json();
})
.then((data) => {
callback(null, data);
})
.catch((error) => {
logger.error(error.message);
callback(error);
});
}

module.exports = makeFetchRequest;
Loading

0 comments on commit aa62a9d

Please sign in to comment.