Skip to content

Instantly share code, notes, and snippets.

@Artawower
Created February 13, 2023 12:43
Show Gist options
  • Save Artawower/3bb46acbc3a8364c084599e81271e94d to your computer and use it in GitHub Desktop.
Save Artawower/3bb46acbc3a8364c084599e81271e94d to your computer and use it in GitHub Desktop.
import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor, HttpResponse } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable, of, timer } from 'rxjs';
import { mergeMap, tap, retryWhen, delayWhen } from 'rxjs/operators';
@Injectable()
/**
* This class is used to intercept network errors with status 500+ and some 400s
* then add failed request to the queue and try to resend it after
* some times.
*/
export class RetryInterceptor implements HttpInterceptor {
private readonly INITIAL_RETRY_DELAY = 5000; // 5 seconds
private readonly MAX_RETRY_DELAY = 15000; // 15 seconds
private readonly httpRequestsWithError: { [key: string]: number } = {};
private readonly retryStatusCodes = [0, 408, 429, 449, 425, 410, 405];
constructor() {}
public intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(req).pipe(
retryWhen((errors) =>
errors.pipe(
mergeMap((error: HttpResponse<any>) => {
console.log('✎: [line 28][retry.interceptor.ts] error: ', error);
if (this.isRetryableRequest(error.status)) {
this.increaseRetryDelay(req);
return of(error);
}
this.clearRetryState(req);
throw error;
}),
delayWhen(() => timer(this.getRetryDelay(req)))
)
),
tap(() => {
this.clearRetryState(req);
})
);
}
private isRetryableRequest(status: number): boolean {
return status >= 500 || this.retryStatusCodes.includes(status);
}
private increaseRetryDelay(req: HttpRequest<any>): void {
const requestKey = this.getUniqueRequestMethod(req);
if (!this.httpRequestsWithError[requestKey]) {
this.httpRequestsWithError[requestKey] = 0;
}
this.httpRequestsWithError[requestKey] += 1;
}
private getUniqueRequestMethod(req: HttpRequest<any>): string {
return `${req.method}_${req.urlWithParams}`;
}
private getRetryDelay(req: HttpRequest<any>): number {
const requestKey = this.getUniqueRequestMethod(req);
const failedCount = this.httpRequestsWithError[requestKey];
const retryDelay = this.INITIAL_RETRY_DELAY * failedCount;
return Math.min(retryDelay, this.MAX_RETRY_DELAY);
}
private clearRetryState(req: HttpRequest<any>): void {
const requestKey = this.getUniqueRequestMethod(req);
delete this.httpRequestsWithError[requestKey];
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment