throttlingRetryPolicy.ts 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
  1. // Copyright (c) Microsoft Corporation. All rights reserved.
  2. // Licensed under the MIT License. See License.txt in the project root for license information.
  3. import { BaseRequestPolicy, RequestPolicy, RequestPolicyOptions, RequestPolicyFactory } from "./requestPolicy";
  4. import { WebResource } from "../webResource";
  5. import { HttpOperationResponse } from "../httpOperationResponse";
  6. import { Constants } from "../util/constants";
  7. import { delay } from "../util/utils";
  8. type ResponseHandler = (httpRequest: WebResource, response: HttpOperationResponse) => Promise<HttpOperationResponse>;
  9. const StatusCodes = Constants.HttpConstants.StatusCodes;
  10. export function throttlingRetryPolicy(): RequestPolicyFactory {
  11. return {
  12. create: (nextPolicy: RequestPolicy, options: RequestPolicyOptions) => {
  13. return new ThrottlingRetryPolicy(nextPolicy, options);
  14. }
  15. };
  16. }
  17. /**
  18. * To learn more, please refer to
  19. * https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-manager-request-limits,
  20. * https://docs.microsoft.com/en-us/azure/azure-subscription-service-limits and
  21. * https://docs.microsoft.com/en-us/azure/virtual-machines/troubleshooting/troubleshooting-throttling-errors
  22. */
  23. export class ThrottlingRetryPolicy extends BaseRequestPolicy {
  24. private _handleResponse: ResponseHandler;
  25. constructor(nextPolicy: RequestPolicy, options: RequestPolicyOptions, _handleResponse?: ResponseHandler) {
  26. super(nextPolicy, options);
  27. this._handleResponse = _handleResponse || this._defaultResponseHandler;
  28. }
  29. public async sendRequest(httpRequest: WebResource): Promise<HttpOperationResponse> {
  30. return this._nextPolicy.sendRequest(httpRequest.clone()).then(response => {
  31. if (response.status !== StatusCodes.TooManyRequests) {
  32. return response;
  33. } else {
  34. return this._handleResponse(httpRequest, response);
  35. }
  36. });
  37. }
  38. private async _defaultResponseHandler(httpRequest: WebResource, httpResponse: HttpOperationResponse): Promise<HttpOperationResponse> {
  39. const retryAfterHeader: string | undefined = httpResponse.headers.get(Constants.HeaderConstants.RETRY_AFTER);
  40. if (retryAfterHeader) {
  41. const delayInMs: number | undefined = ThrottlingRetryPolicy.parseRetryAfterHeader(retryAfterHeader);
  42. if (delayInMs) {
  43. return delay(delayInMs).then((_: any) => this._nextPolicy.sendRequest(httpRequest));
  44. }
  45. }
  46. return httpResponse;
  47. }
  48. public static parseRetryAfterHeader(headerValue: string): number | undefined {
  49. const retryAfterInSeconds = Number(headerValue);
  50. if (Number.isNaN(retryAfterInSeconds)) {
  51. return ThrottlingRetryPolicy.parseDateRetryAfterHeader(headerValue);
  52. } else {
  53. return retryAfterInSeconds * 1000;
  54. }
  55. }
  56. public static parseDateRetryAfterHeader(headerValue: string): number | undefined {
  57. try {
  58. const now: number = Date.now();
  59. const date: number = Date.parse(headerValue);
  60. const diff = date - now;
  61. return Number.isNaN(diff) ? undefined : diff;
  62. } catch (error) {
  63. return undefined;
  64. }
  65. }
  66. }