simplerestclients
Version:
A library of components for accessing RESTful services with javascript/typescript.
713 lines • 34.2 kB
JavaScript
"use strict";
/**
* SimpleWebRequest.ts
* Author: David de Regt
* Copyright: Microsoft 2016
*
* Simple client for issuing web requests.
*/
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
Object.defineProperty(exports, "__esModule", { value: true });
var utils_1 = require("./utils");
var ExponentialTime_1 = require("./ExponentialTime");
var WebRequestPriority;
(function (WebRequestPriority) {
WebRequestPriority[WebRequestPriority["DontCare"] = 0] = "DontCare";
WebRequestPriority[WebRequestPriority["Low"] = 1] = "Low";
WebRequestPriority[WebRequestPriority["Normal"] = 2] = "Normal";
WebRequestPriority[WebRequestPriority["High"] = 3] = "High";
WebRequestPriority[WebRequestPriority["Critical"] = 4] = "Critical";
})(WebRequestPriority = exports.WebRequestPriority || (exports.WebRequestPriority = {}));
var ErrorHandlingType;
(function (ErrorHandlingType) {
// Ignore retry policy, if any, and fail immediately
ErrorHandlingType[ErrorHandlingType["DoNotRetry"] = 0] = "DoNotRetry";
// Retry immediately, without counting it as a failure (used when you've made some sort of change to the )
ErrorHandlingType[ErrorHandlingType["RetryUncountedImmediately"] = 1] = "RetryUncountedImmediately";
// Retry with exponential backoff, but don't count it as a failure (for 429 handling)
ErrorHandlingType[ErrorHandlingType["RetryUncountedWithBackoff"] = 2] = "RetryUncountedWithBackoff";
// Use standard retry policy (count it as a failure, exponential backoff as policy dictates)
ErrorHandlingType[ErrorHandlingType["RetryCountedWithBackoff"] = 3] = "RetryCountedWithBackoff";
// Return this if you need to satisfy some condition before this request will retry (then call .resumeRetrying()).
ErrorHandlingType[ErrorHandlingType["PauseUntilResumed"] = 4] = "PauseUntilResumed";
})(ErrorHandlingType = exports.ErrorHandlingType || (exports.ErrorHandlingType = {}));
function isJsonContentType(ct) {
return !!ct && ct.indexOf('application/json') === 0;
}
function isFormContentType(ct) {
return !!ct && ct.indexOf('application/x-www-form-urlencoded') === 0;
}
function isFormDataContentType(ct) {
return !!ct && ct.indexOf('multipart/form-data') === 0;
}
exports.DefaultOptions = {
priority: WebRequestPriority.Normal,
};
exports.SimpleWebRequestOptions = {
MaxSimultaneousRequests: 5,
HungRequestCleanupIntervalMs: 10000,
setTimeout: function (callback, timeoutMs) { return setTimeout(callback, timeoutMs); },
clearTimeout: function (id) { return clearTimeout(id); },
};
function DefaultErrorHandler(webRequest, errResp) {
if (errResp.canceled || !errResp.statusCode || errResp.statusCode >= 400 && errResp.statusCode < 600) {
// Fail canceled/0/4xx/5xx requests immediately.
// These are permenent failures, and shouldn't have retry logic applied to them.
return ErrorHandlingType.DoNotRetry;
}
// Possible transient failure -- just retry as normal with backoff.
return ErrorHandlingType.RetryCountedWithBackoff;
}
exports.DefaultErrorHandler = DefaultErrorHandler;
// List of pending requests, sorted from most important to least important (numerically descending)
var requestQueue = [];
// List of requests blocked on _blockUNtil promises
var blockedList = [];
// List of executing (non-finished) requests -- only to keep track of number of requests to compare to the max
var executingList = [];
var hungRequestCleanupTimer;
// Feature flag checkers for whether the current environment supports various types of XMLHttpRequest features
var onLoadErrorSupportStatus = 0 /* Unknown */;
var timeoutSupportStatus = 0 /* Unknown */;
var SimpleWebRequestBase = /** @class */ (function () {
function SimpleWebRequestBase(_action, _url, options, _getHeaders, _blockRequestUntil) {
this._action = _action;
this._url = _url;
this.options = options;
this._getHeaders = _getHeaders;
this._blockRequestUntil = _blockRequestUntil;
this._aborted = false;
this._timedOut = false;
this._paused = false;
this._created = Date.now();
// De-dupe result handling for two reasons so far:
// 1. Various platforms have bugs where they double-resolves aborted xmlhttprequests
// 2. Safari seems to have a bug where sometimes it double-resolves happily-completed xmlhttprequests
this._finishHandled = false;
this._retryExponentialTime = new ExponentialTime_1.ExponentialTime(1000, 300000);
this._options = __assign(__assign({}, exports.DefaultOptions), options);
}
SimpleWebRequestBase.prototype.getPriority = function () {
return this._options.priority || WebRequestPriority.DontCare;
};
SimpleWebRequestBase.checkQueueProcessing = function () {
var _loop_1 = function () {
var req = requestQueue.shift();
if (!req) {
return { value: void 0 };
}
blockedList.push(req);
var blockPromise = (req._blockRequestUntil && req._blockRequestUntil()) || Promise.resolve();
blockPromise.then(function () {
utils_1.remove(blockedList, req);
if (executingList.length < exports.SimpleWebRequestOptions.MaxSimultaneousRequests && !req._aborted) {
executingList.push(req);
SimpleWebRequest._scheduleHungRequestCleanupIfNeeded();
req._fire();
}
else {
req._enqueue();
}
}, function (err) {
utils_1.remove(blockedList, req);
// fail the request if the block promise is rejected
req._respond('_blockRequestUntil rejected: ' + err);
});
};
while (executingList.length < exports.SimpleWebRequestOptions.MaxSimultaneousRequests) {
var state_1 = _loop_1();
if (typeof state_1 === "object")
return state_1.value;
}
};
SimpleWebRequestBase._scheduleHungRequestCleanupIfNeeded = function () {
// Schedule a cleanup timer if needed
if (executingList.length > 0 && hungRequestCleanupTimer === undefined) {
hungRequestCleanupTimer = exports.SimpleWebRequestOptions.setTimeout(this._hungRequestCleanupTimerCallback, exports.SimpleWebRequestOptions.HungRequestCleanupIntervalMs);
}
else if (executingList.length === 0 && hungRequestCleanupTimer) {
exports.SimpleWebRequestOptions.clearTimeout(hungRequestCleanupTimer);
hungRequestCleanupTimer = undefined;
}
};
SimpleWebRequestBase.prototype._removeFromQueue = function () {
// Only pull from request queue and executing queue here - pulling from the blocked queue can result in requests
// being queued up multiple times if _respond fires more than once (it shouldn't, but does happen in the wild)
utils_1.remove(executingList, this);
utils_1.remove(requestQueue, this);
};
SimpleWebRequestBase.prototype._assertAndClean = function (expression, message) {
if (!expression) {
this._removeFromQueue();
console.error(message);
utils_1.assert(expression, message);
}
};
// TSLint thinks that this function is unused. Silly tslint.
// tslint:disable-next-line
SimpleWebRequestBase.prototype._fire = function () {
var _this = this;
this._xhr = new XMLHttpRequest();
this._xhrRequestHeaders = {};
// xhr.open() can throw an exception for a CSP violation.
var openError = utils_1.attempt(function () {
// Apparently you're supposed to open the connection before adding events to it. If you don't, the node.js implementation
// of XHR actually calls this.abort() at the start of open()... Bad implementations, hooray.
_this._xhr.open(_this._action, _this._url, true);
});
if (openError) {
this._respond(openError.toString());
return;
}
if (this._options.timeout) {
var timeoutSupported_1 = timeoutSupportStatus;
// Use manual timer if we don't know about timeout support
if (timeoutSupported_1 !== 3 /* Supported */) {
this._assertAndClean(!this._requestTimeoutTimer, 'Double-fired requestTimeoutTimer');
this._requestTimeoutTimer = exports.SimpleWebRequestOptions.setTimeout(function () {
_this._requestTimeoutTimer = undefined;
_this._timedOut = true;
_this.abort();
}, this._options.timeout);
}
// This is our first completed request. Use it for feature detection
if (timeoutSupported_1 === 3 /* Supported */ || timeoutSupported_1 <= 1 /* Detecting */) {
// timeout and ontimeout are part of the XMLHttpRequest Level 2 spec, should be supported in most modern browsers
this._xhr.timeout = this._options.timeout;
this._xhr.ontimeout = function () {
timeoutSupportStatus = 3 /* Supported */;
if (timeoutSupported_1 !== 3 /* Supported */) {
// When this request initially fired we didn't know about support, bail & let the fallback method handle this
return;
}
_this._timedOut = true;
// Set aborted flag to match simple timer approach, which aborts the request and results in an _respond call
_this._aborted = true;
_this._respond('TimedOut');
};
}
}
var onLoadErrorSupported = onLoadErrorSupportStatus;
// Use onreadystatechange if we don't know about onload support or it onload is not supported
if (onLoadErrorSupported !== 3 /* Supported */) {
if (onLoadErrorSupported === 0 /* Unknown */) {
// Set global status to detecting, leave local state so we can set a timer on finish
onLoadErrorSupportStatus = 1 /* Detecting */;
}
this._xhr.onreadystatechange = function () {
if (!_this._xhr) {
return;
}
if (_this._xhr.readyState === 3 && _this._options.streamingDownloadProgress && !_this._aborted) {
// This callback may result in cancelling the connection, so keep that in mind with any handling after it
// if we decide to stop using the return after this someday down the line. i.e. this._xhr may be undefined
// when we come back from this call.
_this._options.streamingDownloadProgress(_this._xhr.responseText);
return;
}
if (_this._xhr.readyState !== 4) {
// Wait for it to finish
return;
}
// This is the first request completed (unknown status when fired, detecting now), use it for detection
if (onLoadErrorSupported === 0 /* Unknown */ &&
onLoadErrorSupportStatus === 1 /* Detecting */) {
// If onload hasn't fired within 10 seconds of completion, detect as not supported
exports.SimpleWebRequestOptions.setTimeout(function () {
if (onLoadErrorSupportStatus !== 3 /* Supported */) {
onLoadErrorSupportStatus = 2 /* NotSupported */;
}
}, 10000);
}
_this._respond();
};
}
else if (this._options.streamingDownloadProgress) {
// If we support onload and such, but have a streaming download handler, still trap the oRSC.
this._xhr.onreadystatechange = function () {
if (!_this._xhr) {
return;
}
if (_this._xhr.readyState === 3 && _this._options.streamingDownloadProgress && !_this._aborted) {
// This callback may result in cancelling the connection, so keep that in mind with any handling after it
// if we decide to stop using the return after this someday down the line. i.e. this._xhr may be undefined
// when we come back from this call.
_this._options.streamingDownloadProgress(_this._xhr.responseText);
}
};
}
if (onLoadErrorSupported !== 2 /* NotSupported */) {
// onLoad and onError are part of the XMLHttpRequest Level 2 spec, should be supported in most modern browsers
this._xhr.onload = function () {
onLoadErrorSupportStatus = 3 /* Supported */;
if (onLoadErrorSupported !== 3 /* Supported */) {
// When this request initially fired we didn't know about support, bail & let the fallback method handle this
return;
}
_this._respond();
};
this._xhr.onerror = function () {
onLoadErrorSupportStatus = 3 /* Supported */;
if (onLoadErrorSupported !== 3 /* Supported */) {
// When this request initially fired we didn't know about support, bail & let the fallback method handle this
return;
}
_this._respond();
};
}
this._xhr.onabort = function () {
// If the browser cancels us (page navigation or whatever), it sometimes calls both the readystatechange and this,
// so make sure we know that this is an abort.
_this._aborted = true;
_this._respond('Aborted');
};
if (this._xhr.upload && this._options.onProgress) {
this._xhr.upload.onprogress = this._options.onProgress;
}
var acceptType = this._options.acceptType || 'json';
var responseType = this._options.customResponseType || SimpleWebRequestBase._getResponseType(acceptType);
var responseTypeError = utils_1.attempt(function () {
_this._xhr.responseType = responseType;
});
if (responseTypeError) {
// WebKit added support for the json responseType value on 09/03/2013
// https://bugs.webkit.org/show_bug.cgi?id=73648.
// Versions of Safari prior to 7 and Android 4 Samsung borwsers are
// known to throw an Error when setting the value "json" as the response type.
//
// The json response type can be ignored if not supported, because JSON payloads
// are handled by mapBody anyway
if (responseType !== 'json') {
throw responseTypeError;
}
}
SimpleWebRequest._setRequestHeader(this._xhr, this._xhrRequestHeaders, 'Accept', SimpleWebRequestBase.mapContentType(acceptType));
this._xhr.withCredentials = !!this._options.withCredentials;
var nextHeaders = this.getRequestHeaders();
// check/process headers
var headersCheck = {};
Object.keys(nextHeaders).forEach(function (key) {
var value = nextHeaders[key];
var headerLower = key.toLowerCase();
if (headerLower === 'content-type') {
_this._assertAndClean(false, "Don't set Content-Type with options.headers -- use it with the options.contentType property");
return;
}
if (headerLower === 'accept') {
_this._assertAndClean(false, "Don't set Accept with options.headers -- use it with the options.acceptType property");
return;
}
_this._assertAndClean(!headersCheck[headerLower], "Setting duplicate header key: " + headersCheck[headerLower] + " and " + key);
if (value === undefined || value === null) {
console.warn("Tried to set header \"" + key + "\" on request with \"" + value + "\" value, header will be dropped");
return;
}
headersCheck[headerLower] = true;
SimpleWebRequest._setRequestHeader(_this._xhr, _this._xhrRequestHeaders, key, value);
});
if (this._options.sendData) {
var contentType = SimpleWebRequestBase.mapContentType(this._options.contentType || 'json');
SimpleWebRequest._setRequestHeader(this._xhr, this._xhrRequestHeaders, 'Content-Type', contentType);
var sendData = SimpleWebRequestBase.mapBody(this._options.sendData, contentType);
this._xhr.send(sendData);
}
else {
this._xhr.send();
}
};
SimpleWebRequestBase._setRequestHeader = function (xhr, xhrRequestHeaders, key, val) {
xhr.setRequestHeader(key, val);
xhrRequestHeaders[key] = val;
};
SimpleWebRequestBase.mapContentType = function (contentType) {
if (contentType === 'json') {
return 'application/json';
}
else if (contentType === 'form') {
return 'application/x-www-form-urlencoded';
}
else {
return contentType;
}
};
SimpleWebRequestBase.mapBody = function (sendData, contentType) {
if (isJsonContentType(contentType)) {
if (!utils_1.isString(sendData)) {
return JSON.stringify(sendData);
}
}
else if (isFormContentType(contentType)) {
if (!utils_1.isString(sendData) && utils_1.isObject(sendData)) {
var params_1 = sendData;
return Object.keys(params_1)
.map(function (param) { return encodeURIComponent(param) + (params_1[param] ? '=' + encodeURIComponent(params_1[param].toString()) : ''); })
.join('&');
}
}
else if (isFormDataContentType(contentType)) {
if (utils_1.isObject(sendData)) {
// Note: This only works for IE10 and above.
var formData_1 = new FormData();
var params_2 = sendData;
Object.keys(params_2)
.forEach(function (param) { return formData_1.append(param, params_2[param]); });
return formData_1;
}
else {
utils_1.assert(false, 'contentType multipart/form-data must include an object as sendData');
}
}
return sendData;
};
SimpleWebRequestBase.prototype.setUrl = function (newUrl) {
this._url = newUrl;
};
SimpleWebRequestBase.prototype.setHeader = function (key, val) {
if (!this._options.augmentHeaders) {
this._options.augmentHeaders = {};
}
if (val) {
this._options.augmentHeaders[key] = val;
}
else {
delete this._options.augmentHeaders[key];
}
};
SimpleWebRequestBase.prototype.getRequestHeaders = function () {
var headers = {};
if (this._getHeaders && !this._options.overrideGetHeaders && !this._options.headers) {
headers = __assign(__assign({}, headers), this._getHeaders());
}
if (this._options.overrideGetHeaders) {
headers = __assign(__assign({}, headers), this._options.overrideGetHeaders);
}
if (this._options.headers) {
headers = __assign(__assign({}, headers), this._options.headers);
}
if (this._options.augmentHeaders) {
headers = __assign(__assign({}, headers), this._options.augmentHeaders);
}
return headers;
};
SimpleWebRequestBase.prototype.getOptions = function () {
return utils_1.clone(this._options);
};
SimpleWebRequestBase.prototype.setPriority = function (newPriority) {
if (this._options.priority === newPriority) {
return;
}
this._options.priority = newPriority;
if (this._paused) {
return;
}
if (this._xhr) {
// Already fired -- wait for it to retry for the new priority to matter
return;
}
// Remove and re-queue
utils_1.remove(requestQueue, this);
this._enqueue();
};
SimpleWebRequestBase.prototype.resumeRetrying = function () {
if (!this._paused) {
utils_1.assert(false, 'resumeRetrying() called but not paused!');
return;
}
this._paused = false;
this._enqueue();
};
SimpleWebRequestBase.prototype._enqueue = function () {
var _this = this;
// It's possible for a request to be canceled before it's queued since onCancel fires synchronously and we set up the listener
// before queueing for execution
// An aborted request should never be queued for execution
if (this._aborted) {
return;
}
// Check if the current queues, if the request is already in there, nothing to enqueue
if (executingList.indexOf(this) >= 0 || blockedList.indexOf(this) >= 0 || requestQueue.indexOf(this) >= 0) {
return;
}
// Throw it on the queue
var index = requestQueue.findIndex(function (request) {
// find a request with the same priority, but newer
return (request.getPriority() === _this.getPriority() && request._created > _this._created) ||
// or a request with lower priority
(request.getPriority() < _this.getPriority());
});
if (index > -1) {
// add me before the found request
requestQueue.splice(index, 0, this);
}
else {
// add me at the end
requestQueue.push(this);
}
// See if it's time to execute it
SimpleWebRequestBase.checkQueueProcessing();
};
SimpleWebRequestBase._getResponseType = function (acceptType) {
if (acceptType === 'blob') {
return 'arraybuffer';
}
if (acceptType === 'text/xml' || acceptType === 'application/xml') {
return 'document';
}
if (acceptType === 'text/plain') {
return 'text';
}
return 'json';
};
SimpleWebRequestBase._hungRequestCleanupTimerCallback = function () {
hungRequestCleanupTimer = undefined;
executingList.filter(function (request) {
if (request._xhr && request._xhr.readyState === 4) {
console.warn('SimpleWebRequests found a completed XHR that hasn\'t invoked it\'s callback functions, manually responding');
return true;
}
return false;
}).forEach(function (request) {
// We need to respond outside of the initial iteration across the array since _respond mutates exeutingList
request._respond();
});
SimpleWebRequest._scheduleHungRequestCleanupIfNeeded();
};
return SimpleWebRequestBase;
}());
exports.SimpleWebRequestBase = SimpleWebRequestBase;
var SimpleWebRequest = /** @class */ (function (_super) {
__extends(SimpleWebRequest, _super);
function SimpleWebRequest(action, url, options, getHeaders, blockRequestUntil) {
return _super.call(this, action, url, options, getHeaders, blockRequestUntil) || this;
}
SimpleWebRequest.prototype.abort = function () {
if (this._aborted) {
utils_1.assert(false, 'Already aborted ' + this._action + ' request to ' + this._url);
return;
}
this._aborted = true;
if (this._retryTimer) {
exports.SimpleWebRequestOptions.clearTimeout(this._retryTimer);
this._retryTimer = undefined;
}
if (this._requestTimeoutTimer) {
exports.SimpleWebRequestOptions.clearTimeout(this._requestTimeoutTimer);
this._requestTimeoutTimer = undefined;
}
if (!this._resolve) {
utils_1.assert(false, 'Haven\'t even fired start() yet -- can\'t abort');
return;
}
// Cannot rely on this._xhr.abort() to trigger this._xhr.onAbort() synchronously, thus we must trigger an early response here
this._respond('Aborted');
if (this._xhr) {
// Abort the in-flight request
this._xhr.abort();
}
};
SimpleWebRequest.prototype.start = function () {
var _this = this;
if (this._resolve) {
utils_1.assert(false, 'WebRequest already started');
return Promise.reject('WebRequest already started');
}
var promise = new Promise(function (res, rej) {
_this._resolve = res;
_this._reject = rej;
});
this._enqueue();
return promise;
};
SimpleWebRequest.prototype._respond = function (errorStatusText) {
var _this = this;
if (this._finishHandled) {
// Aborted web requests often double-finish due to odd browser behavior, but non-aborted requests shouldn't...
// Unfortunately, this assertion fires frequently in the Safari browser, presumably due to a non-standard
// XHR implementation, so we need to comment it out.
// This also might get hit during browser feature detection process
// assert(this._aborted || this._timedOut, 'Double-finished XMLHttpRequest');
return;
}
this._finishHandled = true;
this._removeFromQueue();
if (this._retryTimer) {
exports.SimpleWebRequestOptions.clearTimeout(this._retryTimer);
this._retryTimer = undefined;
}
if (this._requestTimeoutTimer) {
exports.SimpleWebRequestOptions.clearTimeout(this._requestTimeoutTimer);
this._requestTimeoutTimer = undefined;
}
var statusCode = 0;
var statusText;
if (this._xhr) {
try {
statusCode = this._xhr.status;
statusText = this._xhr.statusText || errorStatusText;
}
catch (e) {
// Some browsers error when you try to read status off aborted requests
}
}
else {
statusText = errorStatusText || 'Browser Error - Possible CORS or Connectivity Issue';
}
var headers = {};
var body;
var responseParsingException;
// Build the response info
if (this._xhr) {
// Parse out headers
var headerLines = (this._xhr.getAllResponseHeaders() || '').split(/\r?\n/);
headerLines.forEach(function (line) {
if (line.length === 0) {
return;
}
var index = line.indexOf(':');
if (index === -1) {
headers[line] = '';
}
else {
headers[line.substr(0, index).toLowerCase()] = line.substr(index + 1).trim();
}
});
// Some browsers apparently don't set the content-type header in some error conditions from getAllResponseHeaders but do return
// it from the normal getResponseHeader. No clue why, but superagent mentions it as well so it's best to just conform.
if (!headers['content-type']) {
var check = this._xhr.getResponseHeader('content-type');
if (check) {
headers['content-type'] = check;
}
}
body = this._xhr.response;
if (headers['content-type'] && isJsonContentType(headers['content-type'])) {
if (!body || !utils_1.isObject(body)) {
// Response can be null if the responseType does not match what the server actually sends
// https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/responseType
// Only access responseText if responseType is "text" or "", otherwise it will throw
// https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/responseText
if ((this._xhr.responseType === 'text' || this._xhr.responseType === '') && this._xhr.responseText) {
try {
body = JSON.parse(this._xhr.responseText);
}
catch (ex) {
// If a service returns invalid JSON in a payload, we can end up here - don't crash
// responseParsingException flag will indicate that we got response from the server that was corrupted.
// This will be manifested as null on receipient side and flag can help in understanding the problem.
responseParsingException = ex;
console.warn('Failed to parse XHR JSON response');
}
}
}
}
}
if (this._xhr && this._xhr.readyState === 4 && ((statusCode >= 200 && statusCode < 300) || statusCode === 304)) {
// Happy path!
var resp = {
url: this._xhr.responseURL || this._url,
method: this._action,
requestOptions: this._options,
requestHeaders: this._xhrRequestHeaders || {},
statusCode: statusCode,
statusText: statusText,
headers: headers,
body: body,
responseParsingException: responseParsingException,
};
this._resolve(resp);
}
else {
var errResp = {
url: (this._xhr ? this._xhr.responseURL : undefined) || this._url,
method: this._action,
requestOptions: this._options,
requestHeaders: this._xhrRequestHeaders || {},
statusCode: statusCode,
statusText: statusText,
headers: headers,
body: body,
canceled: this._aborted,
timedOut: this._timedOut,
responseParsingException: responseParsingException,
};
if (this._options.augmentErrorResponse) {
this._options.augmentErrorResponse(errResp);
}
// Policy-adaptable failure
var handleResponse = this._options.customErrorHandler
? this._options.customErrorHandler(this, errResp)
: DefaultErrorHandler(this, errResp);
var retry = handleResponse !== ErrorHandlingType.DoNotRetry && ((this._options.retries && this._options.retries > 0) ||
handleResponse === ErrorHandlingType.PauseUntilResumed ||
handleResponse === ErrorHandlingType.RetryUncountedImmediately ||
handleResponse === ErrorHandlingType.RetryUncountedWithBackoff);
if (retry) {
if (handleResponse === ErrorHandlingType.RetryCountedWithBackoff) {
this._options.retries--;
}
if (this._requestTimeoutTimer) {
exports.SimpleWebRequestOptions.clearTimeout(this._requestTimeoutTimer);
this._requestTimeoutTimer = undefined;
}
this._aborted = false;
this._timedOut = false;
this._finishHandled = false;
// Clear the XHR since we technically just haven't started again yet...
if (this._xhr) {
this._xhr.onabort = null;
this._xhr.onerror = null;
this._xhr.onload = null;
this._xhr.onprogress = null;
this._xhr.onreadystatechange = null;
this._xhr.ontimeout = null;
this._xhr = undefined;
this._xhrRequestHeaders = undefined;
}
if (handleResponse === ErrorHandlingType.PauseUntilResumed) {
this._paused = true;
}
else if (handleResponse === ErrorHandlingType.RetryUncountedImmediately) {
this._enqueue();
}
else {
this._retryTimer = exports.SimpleWebRequestOptions.setTimeout(function () {
_this._retryTimer = undefined;
_this._enqueue();
}, this._retryExponentialTime.getTimeAndCalculateNext());
}
}
else {
// No more retries -- fail.
this._reject(errResp);
}
}
// Freed up a spot, so let's see if there's other stuff pending
SimpleWebRequestBase.checkQueueProcessing();
};
return SimpleWebRequest;
}(SimpleWebRequestBase));
exports.SimpleWebRequest = SimpleWebRequest;
//# sourceMappingURL=SimpleWebRequest.js.map