Secure your code as it's written. Use Snyk Code to scan source code in minutes - no build needed - and fix issues immediately.
// We try to prevent such cases through early validation, e.g. validate sub request count >= 1.
// While in unexpected sub request invalid case, we allow sub response to be parsed and return to user.
if (subResponseCount != this.subRequests.size && subResponseCount != 1) {
throw new Error("Invalid state: sub responses' count is not equal to sub requests' count.");
}
let deserializedSubResponses: Array = new Array(subResponseCount);
let subResponsesSucceededCount: number = 0;
let subResponsesFailedCount: number = 0;
// Parse sub subResponses.
for (let index = 0; index < subResponseCount; index++) {
const subResponse = subResponses[index];
deserializedSubResponses[index] = {} as BatchSubResponse;
let deserializedSubResponse = deserializedSubResponses[index];
deserializedSubResponse.headers = new HttpHeaders();
let responseLines = subResponse.split(`${HTTP_LINE_ENDING}`);
let subRespHeaderStartFound = false;
let subRespHeaderEndFound = false;
let subRespFailed = false;
let contentId = NOT_FOUND;
for (const responseLine of responseLines) {
if (!subRespHeaderStartFound) {
// Convention line to indicate content ID
if (responseLine.startsWith(HeaderConstants.CONTENT_ID)) {
contentId = parseInt(responseLine.split(HTTP_HEADER_DELIMITER)[1]);
}
// Http version line with status code indicates the start of sub request's response.
// Example: HTTP/1.1 202 Accepted
export function getURLPathAndQuery(url: string): string | undefined {
const urlParsed = URLBuilder.parse(url);
const pathString = urlParsed.getPath();
if (!pathString) {
throw new RangeError("Invalid url without valid path.");
}
let queryString = urlParsed.getQuery() || "";
queryString = queryString.trim();
if (queryString != "") {
queryString = queryString.startsWith("?") ? queryString : `?${queryString}`; // Ensure query string start with '?'
}
return `${pathString}${queryString}`;
}
public async sendRequest(request: WebResource): Promise {
this.tryCount++;
this.requestStartTime = new Date();
if (this.tryCount === 1) {
this.operationStartTime = this.requestStartTime;
}
this.log(HttpPipelineLogLevel.INFO, `==> OUTGOING REQUEST (Try number=${this.tryCount}):`);
this.log(HttpPipelineLogLevel.INFO, ` ${request.method}: ${sanitizeURL(request.url)}`);
try {
const response = await this._nextPolicy.sendRequest(request);
const requestEndTime = new Date();
const requestCompletionTime = requestEndTime.getTime() - this.requestStartTime.getTime();
const operationDuration = requestEndTime.getTime() - this.operationStartTime.getTime();
let currentLevel: HttpPipelineLogLevel = HttpPipelineLogLevel.INFO;
let logMessage: string = "";
if (this.shouldLog(HttpPipelineLogLevel.INFO)) {
// Assume success and default to informational logging.
logMessage = "Successfully Received Response. ";
}
// If the response took too long, we'll upgrade to warning.
this.logf(
HttpPipelineLogLevel.INFO,
`RetryPolicy: Network error ${retriableError} found, will retry.`
);
return true;
}
}
}
// If attempt was against the secondary & it returned a StatusNotFound (404), then
// the resource was not found. This may be due to replication delay. So, in this
// case, we'll never try the secondary again for this operation.
if (response || err) {
const statusCode = response ? response.status : err ? err.statusCode : 0;
if (!isPrimaryRetry && statusCode === 404) {
this.logf(HttpPipelineLogLevel.INFO, `RetryPolicy: Secondary access with 404, will retry.`);
return true;
}
// Server internal error or server timeout
if (statusCode === 503 || statusCode === 500) {
this.logf(
HttpPipelineLogLevel.INFO,
`RetryPolicy: Will retry for status code ${statusCode}.`
);
return true;
}
}
return false;
}
public async sendRequest(request: WebResource): Promise {
this.tryCount++;
this.requestStartTime = new Date();
if (this.tryCount === 1) {
this.operationStartTime = this.requestStartTime;
}
this.log(HttpPipelineLogLevel.INFO, `==> OUTGOING REQUEST (Try number=${this.tryCount}):`);
this.log(HttpPipelineLogLevel.INFO, ` ${request.method}: ${sanitizeURL(request.url)}`);
try {
const response = await this._nextPolicy.sendRequest(request);
const requestEndTime = new Date();
const requestCompletionTime = requestEndTime.getTime() - this.requestStartTime.getTime();
const operationDuration = requestEndTime.getTime() - this.operationStartTime.getTime();
let currentLevel: HttpPipelineLogLevel = HttpPipelineLogLevel.INFO;
let logMessage: string = "";
if (this.shouldLog(HttpPipelineLogLevel.INFO)) {
// Assume success and default to informational logging.
logMessage = "Successfully Received Response. ";
}
const isPrimaryRetry = true; // File doesn't suport secondary endpoint
// Set the server-side timeout query parameter "timeout=[seconds]"
if (this.retryOptions.tryTimeoutInMs) {
newRequest.url = setURLParameter(
newRequest.url,
URLConstants.Parameters.TIMEOUT,
Math.floor(this.retryOptions.tryTimeoutInMs! / 1000).toString()
);
}
let response: HttpOperationResponse | undefined;
try {
this.logf(
HttpPipelineLogLevel.INFO,
`RetryPolicy: =====> Try=${attempt} ${isPrimaryRetry ? "Primary" : "Secondary"}`
);
response = await this._nextPolicy.sendRequest(newRequest);
if (!this.shouldRetry(isPrimaryRetry, attempt, response)) {
return response;
}
secondaryHas404 = secondaryHas404 || (!isPrimaryRetry && response.status === 404);
} catch (err) {
this.logf(
HttpPipelineLogLevel.ERROR,
`RetryPolicy: Caught error, message: ${err.message}, code: ${err.code}`
);
if (!this.shouldRetry(isPrimaryRetry, attempt, response, err)) {
throw err;
}
this.requestStartTime = new Date();
if (this.tryCount === 1) {
this.operationStartTime = this.requestStartTime;
}
this.log(HttpPipelineLogLevel.INFO, `==> OUTGOING REQUEST (Try number=${this.tryCount}):`);
this.log(HttpPipelineLogLevel.INFO, ` ${request.method}: ${sanitizeURL(request.url)}`);
try {
const response = await this._nextPolicy.sendRequest(request);
const requestEndTime = new Date();
const requestCompletionTime = requestEndTime.getTime() - this.requestStartTime.getTime();
const operationDuration = requestEndTime.getTime() - this.operationStartTime.getTime();
let currentLevel: HttpPipelineLogLevel = HttpPipelineLogLevel.INFO;
let logMessage: string = "";
if (this.shouldLog(HttpPipelineLogLevel.INFO)) {
// Assume success and default to informational logging.
logMessage = "Successfully Received Response. ";
}
// If the response took too long, we'll upgrade to warning.
if (requestCompletionTime >= this.loggingOptions.logWarningIfTryOverThreshold) {
// Log a warning if the try duration exceeded the specified threshold.
if (this.shouldLog(HttpPipelineLogLevel.WARNING)) {
currentLevel = HttpPipelineLogLevel.WARNING;
logMessage = `SLOW OPERATION. Duration > ${this.loggingOptions.logWarningIfTryOverThreshold} ms. `;
}
}
if (
protected shouldRetry(
isPrimaryRetry: boolean,
attempt: number,
response?: HttpOperationResponse,
err?: RestError
): boolean {
if (attempt >= this.retryOptions.maxTries!) {
this.logf(
HttpPipelineLogLevel.INFO,
`RetryPolicy: Attempt(s) ${attempt} >= maxTries ${this.retryOptions
.maxTries!}, no further try.`
);
return false;
}
// Handle network failures, you may need to customize the list when you implement
// your own http client
const retriableErrors = [
"ETIMEDOUT",
"ESOCKETTIMEDOUT",
"ECONNREFUSED",
"ECONNRESET",
"ENOENT",
"ENOTFOUND",
"TIMEOUT",
public async sendRequest(request: WebResource): Promise {
this.tryCount++;
this.requestStartTime = new Date();
if (this.tryCount === 1) {
this.operationStartTime = this.requestStartTime;
}
let safeURL: string = request.url;
if (getURLParameter(safeURL, URLConstants.Parameters.SIGNATURE)) {
safeURL = setURLParameter(safeURL, URLConstants.Parameters.SIGNATURE, "*****");
}
this.log(
HttpPipelineLogLevel.INFO,
`'${safeURL}'==> OUTGOING REQUEST (Try number=${this.tryCount}).`
);
try {
const response = await this._nextPolicy.sendRequest(request);
const requestEndTime = new Date();
const requestCompletionTime = requestEndTime.getTime() - this.requestStartTime.getTime();
const operationDuration = requestEndTime.getTime() - this.operationStartTime.getTime();
let currentLevel: HttpPipelineLogLevel = HttpPipelineLogLevel.INFO;
let logMessage: string = "";
if (this.shouldLog(HttpPipelineLogLevel.INFO)) {
// Assume success and default to informational logging.
logMessage = "Successfully Received Response. ";
}
"TIMEOUT",
"REQUEST_SEND_ERROR" // For default xhr based http client provided in ms-rest-js
];
if (err) {
for (const retriableError of retriableErrors) {
if (
err.name.toUpperCase().includes(retriableError) ||
err.message.toUpperCase().includes(retriableError) ||
(err.code &&
err.code
.toString()
.toUpperCase()
.includes(retriableError))
) {
this.logf(
HttpPipelineLogLevel.INFO,
`RetryPolicy: Network error ${retriableError} found, will retry.`
);
return true;
}
}
}
// If attempt was against the secondary & it returned a StatusNotFound (404), then
// the resource was not found. This may be due to replication delay. So, in this
// case, we'll never try the secondary again for this operation.
if (response || err) {
const statusCode = response ? response.status : err ? err.statusCode : 0;
if (!isPrimaryRetry && statusCode === 404) {
this.logf(HttpPipelineLogLevel.INFO, `RetryPolicy: Secondary access with 404, will retry.`);
return true;
}