UNPKG

9.64 kBJavaScriptView Raw
1"use strict";
2/**
3 * -------------------------------------------------------------------------------------------
4 * Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License.
5 * See License in the project root for license information.
6 * -------------------------------------------------------------------------------------------
7 */
8Object.defineProperty(exports, "__esModule", { value: true });
9var tslib_1 = require("tslib");
10var RequestMethod_1 = require("../RequestMethod");
11var MiddlewareControl_1 = require("./MiddlewareControl");
12var MiddlewareUtil_1 = require("./MiddlewareUtil");
13var RetryHandlerOptions_1 = require("./options/RetryHandlerOptions");
14var TelemetryHandlerOptions_1 = require("./options/TelemetryHandlerOptions");
15/**
16 * @class
17 * @implements Middleware
18 * Class for RetryHandler
19 */
20var RetryHandler = /** @class */ (function () {
21 /**
22 * @public
23 * @constructor
24 * To create an instance of RetryHandler
25 * @param {RetryHandlerOptions} [options = new RetryHandlerOptions()] - The retry handler options value
26 * @returns An instance of RetryHandler
27 */
28 function RetryHandler(options) {
29 if (options === void 0) { options = new RetryHandlerOptions_1.RetryHandlerOptions(); }
30 this.options = options;
31 }
32 /**
33 *
34 * @private
35 * To check whether the response has the retry status code
36 * @param {Response} response - The response object
37 * @returns Whether the response has retry status code or not
38 */
39 RetryHandler.prototype.isRetry = function (response) {
40 return RetryHandler.RETRY_STATUS_CODES.indexOf(response.status) !== -1;
41 };
42 /**
43 * @private
44 * To check whether the payload is buffered or not
45 * @param {RequestInfo} request - The url string or the request object value
46 * @param {FetchOptions} options - The options of a request
47 * @returns Whether the payload is buffered or not
48 */
49 RetryHandler.prototype.isBuffered = function (request, options) {
50 var method = typeof request === "string" ? options.method : request.method;
51 var isPutPatchOrPost = method === RequestMethod_1.RequestMethod.PUT || method === RequestMethod_1.RequestMethod.PATCH || method === RequestMethod_1.RequestMethod.POST;
52 if (isPutPatchOrPost) {
53 var isStream = MiddlewareUtil_1.getRequestHeader(request, options, "Content-Type") === "application/octet-stream";
54 if (isStream) {
55 return false;
56 }
57 }
58 return true;
59 };
60 /**
61 * @private
62 * To get the delay for a retry
63 * @param {Response} response - The response object
64 * @param {number} retryAttempts - The current attempt count
65 * @param {number} delay - The delay value in seconds
66 * @returns A delay for a retry
67 */
68 RetryHandler.prototype.getDelay = function (response, retryAttempts, delay) {
69 var getRandomness = function () { return Number(Math.random().toFixed(3)); };
70 var retryAfter = response.headers !== undefined ? response.headers.get(RetryHandler.RETRY_AFTER_HEADER) : null;
71 var newDelay;
72 if (retryAfter !== null) {
73 // tslint:disable: prefer-conditional-expression
74 if (Number.isNaN(Number(retryAfter))) {
75 newDelay = Math.round((new Date(retryAfter).getTime() - Date.now()) / 1000);
76 }
77 else {
78 newDelay = Number(retryAfter);
79 }
80 // tslint:enable: prefer-conditional-expression
81 }
82 else {
83 // Adding randomness to avoid retrying at a same
84 newDelay = retryAttempts >= 2 ? this.getExponentialBackOffTime(retryAttempts) + delay + getRandomness() : delay + getRandomness();
85 }
86 return Math.min(newDelay, this.options.getMaxDelay() + getRandomness());
87 };
88 /**
89 * @private
90 * To get an exponential back off value
91 * @param {number} attempts - The current attempt count
92 * @returns An exponential back off value
93 */
94 RetryHandler.prototype.getExponentialBackOffTime = function (attempts) {
95 return Math.round((1 / 2) * (Math.pow(2, attempts) - 1));
96 };
97 /**
98 * @private
99 * @async
100 * To add delay for the execution
101 * @param {number} delaySeconds - The delay value in seconds
102 * @returns Nothing
103 */
104 RetryHandler.prototype.sleep = function (delaySeconds) {
105 return tslib_1.__awaiter(this, void 0, void 0, function () {
106 var delayMilliseconds;
107 return tslib_1.__generator(this, function (_a) {
108 delayMilliseconds = delaySeconds * 1000;
109 return [2 /*return*/, new Promise(function (resolve) { return setTimeout(resolve, delayMilliseconds); })];
110 });
111 });
112 };
113 RetryHandler.prototype.getOptions = function (context) {
114 var options;
115 if (context.middlewareControl instanceof MiddlewareControl_1.MiddlewareControl) {
116 options = context.middlewareControl.getMiddlewareOptions(this.options.constructor);
117 }
118 if (typeof options === "undefined") {
119 options = Object.assign(new RetryHandlerOptions_1.RetryHandlerOptions(), this.options);
120 }
121 return options;
122 };
123 /**
124 * @private
125 * @async
126 * To execute the middleware with retries
127 * @param {Context} context - The context object
128 * @param {number} retryAttempts - The current attempt count
129 * @param {RetryHandlerOptions} options - The retry middleware options instance
130 * @returns A Promise that resolves to nothing
131 */
132 RetryHandler.prototype.executeWithRetry = function (context, retryAttempts, options) {
133 return tslib_1.__awaiter(this, void 0, void 0, function () {
134 var delay, error_1;
135 return tslib_1.__generator(this, function (_a) {
136 switch (_a.label) {
137 case 0:
138 _a.trys.push([0, 6, , 7]);
139 return [4 /*yield*/, this.nextMiddleware.execute(context)];
140 case 1:
141 _a.sent();
142 if (!(retryAttempts < options.maxRetries && this.isRetry(context.response) && this.isBuffered(context.request, context.options) && options.shouldRetry(options.delay, retryAttempts, context.request, context.options, context.response))) return [3 /*break*/, 4];
143 ++retryAttempts;
144 MiddlewareUtil_1.setRequestHeader(context.request, context.options, RetryHandler.RETRY_ATTEMPT_HEADER, retryAttempts.toString());
145 delay = this.getDelay(context.response, retryAttempts, options.delay);
146 return [4 /*yield*/, this.sleep(delay)];
147 case 2:
148 _a.sent();
149 return [4 /*yield*/, this.executeWithRetry(context, retryAttempts, options)];
150 case 3: return [2 /*return*/, _a.sent()];
151 case 4: return [2 /*return*/];
152 case 5: return [3 /*break*/, 7];
153 case 6:
154 error_1 = _a.sent();
155 throw error_1;
156 case 7: return [2 /*return*/];
157 }
158 });
159 });
160 };
161 /**
162 * @public
163 * @async
164 * To execute the current middleware
165 * @param {Context} context - The context object of the request
166 * @returns A Promise that resolves to nothing
167 */
168 RetryHandler.prototype.execute = function (context) {
169 return tslib_1.__awaiter(this, void 0, void 0, function () {
170 var retryAttempts, options, error_2;
171 return tslib_1.__generator(this, function (_a) {
172 switch (_a.label) {
173 case 0:
174 _a.trys.push([0, 2, , 3]);
175 retryAttempts = 0;
176 options = this.getOptions(context);
177 TelemetryHandlerOptions_1.TelemetryHandlerOptions.updateFeatureUsageFlag(context, TelemetryHandlerOptions_1.FeatureUsageFlag.RETRY_HANDLER_ENABLED);
178 return [4 /*yield*/, this.executeWithRetry(context, retryAttempts, options)];
179 case 1: return [2 /*return*/, _a.sent()];
180 case 2:
181 error_2 = _a.sent();
182 throw error_2;
183 case 3: return [2 /*return*/];
184 }
185 });
186 });
187 };
188 /**
189 * @public
190 * To set the next middleware in the chain
191 * @param {Middleware} next - The middleware instance
192 * @returns Nothing
193 */
194 RetryHandler.prototype.setNext = function (next) {
195 this.nextMiddleware = next;
196 };
197 /**
198 * @private
199 * @static
200 * A list of status codes that needs to be retried
201 */
202 RetryHandler.RETRY_STATUS_CODES = [
203 429,
204 503,
205 504,
206 ];
207 /**
208 * @private
209 * @static
210 * A member holding the name of retry attempt header
211 */
212 RetryHandler.RETRY_ATTEMPT_HEADER = "Retry-Attempt";
213 /**
214 * @private
215 * @static
216 * A member holding the name of retry after header
217 */
218 RetryHandler.RETRY_AFTER_HEADER = "Retry-After";
219 return RetryHandler;
220}());
221exports.RetryHandler = RetryHandler;
222//# sourceMappingURL=RetryHandler.js.map
\No newline at end of file