I'm using Angular 1.3.15 and I want to prevent the client code to go into an infinite error loop by handling exceptions properly.
I'm using Sentry for reporting exceptions, but I'm not communicating with Sentry from the client code. Instead I'm reporting exceptions from the client code to a Web API (using XHR), which then reports it to Sentry.
What happened few days ago was that someone visited our site on FireFox 10, so the client code went into an infinite error loop because of badly supported JavaScript, so it literally spammed/DoS'd our API with exceptions. Our API is behind NetScaler so we decided to set up a rate-limiter, and if the requests hit the rate-limiter we now return a 503 HTTP status code (Service Unavailable).
The error looked something like this:
([object Object],[object Object],(void 0))@/Static/Scripts/app/vendor/angular-translate/angular-translate.min.js:6 applyDirectivesToNode([object Array],[object HTMLAnchorElement],[object Object],(void 0),(void 0),null,[object Array],[object Array],[object Object])@/Static/Scripts/app/vendor/angular/angular.js:7498 compileNodes([object Proxy],(void 0))@/Static/Scripts/app/vendor/angular/angular.js:7040 compileNodes([object Proxy],(void 0))@/Static/Scripts/app/vendor/angular/angular.js:7054 compileNodes([object Proxy],(void 0))@/Static/Scripts/app/vendor/angular/angular.js:7054 compileNodes([object Proxy],(void 0))@/Static/Scripts/app/vendor/angular/angular.js:7054 compileNodes([object Proxy],(void 0))@/Static/Scripts/app/vendor/angular/angular.js:7054 compileNodes([object Object],(void 0),[object Object],(void 0),(void 0),null)@/Static/Scripts/app/vendor/angular/angular.js:7054 ...
Now to the problem. I want to implement a proper exception handling in the client code, so it continues to report errors to the API/Sentry, but if it receives HTTP 503 status code response, it should immediately stop script execution and halt.
So I tried to implement an $exceptionHandler
decorator in my Angular module config:
// Flag indicating that the JavaScript execution should be stopped and Sentry should NOT be notified
var stopExecution = false;
$provide.decorator("$exceptionHandler", ['$delegate', function($delegate) {
return function(exception, cause) {
$delegate(exception, cause);
if(stopExecution) return;
// Call Sentry and report the exception
SentryServiceProvider.$get().captureException(exception)
.error(function (data, status, headers, config) {
if(status === 503) {
if(stopExecution) return;
stopExecution = true;
throw new Error('GOT AN 503 ERROR FROM API, STOPPING EXECUTION');
}
});
};
}]);
My captureException()
function in SentryServiceProvider just calls the API with a POST request and sends the exception message and stack trace in a JSON object as a payload, and returns a $http promise.
sentryService.captureException = function (ex) {
return $http.post(API_URL + '/sentry', { Message: ex.message, StackTrace: ex.stack });
};
Now the problem is because it's in an infinite loop, it never goes to the error()
callback and keeps spamming the API forever. How could I do this to prevent infinite error loops in my client code, so it halts when the API says so?