Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion common/config/rush/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions sdk/core/core-rest-pipeline/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

### Bugs Fixed

- [Bug #20778](https://github.com/Azure/azure-sdk-for-js/pull/20778) Customers can provide abort signals in the options bags for the client libraries but they were not being checked when requests were being retried. The issue is fixed in [#20781](https://github.com/Azure/azure-sdk-for-js/pull/20781).

### Other Changes

- Changed the default number of retries from 10 to 3.
Expand Down
3 changes: 3 additions & 0 deletions sdk/core/core-rest-pipeline/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -101,11 +101,13 @@
"@microsoft/api-extractor": "^7.18.11",
"@opentelemetry/api": "^1.0.1",
"@types/chai": "^4.1.6",
"@types/chai-as-promised": "^7.1.0",
"@types/mocha": "^7.0.2",
"@types/node": "^12.0.0",
"@types/sinon": "^9.0.4",
"@types/uuid": "^8.0.0",
"chai": "^4.2.0",
"chai-as-promised": "^7.1.1",
"downlevel-dts": "^0.8.0",
"cross-env": "^7.0.2",
"eslint": "^7.15.0",
Expand All @@ -124,6 +126,7 @@
"mocha-junit-reporter": "^2.0.0",
"mocha": "^7.1.1",
"prettier": "^2.5.1",
"puppeteer": "^13.5.1",
Comment thread
deyaaeldeen marked this conversation as resolved.
"rimraf": "^3.0.0",
"sinon": "^9.0.2",
"source-map-support": "^0.5.9",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ import { PipelineRetryOptions } from "./interfaces";
import { Pipeline, createEmptyPipeline } from "./pipeline";
import { decompressResponsePolicy } from "./policies/decompressResponsePolicy";
import { formDataPolicy } from "./policies/formDataPolicy";
import { logPolicy, LogPolicyOptions } from "./policies/logPolicy";
import { LogPolicyOptions, logPolicy } from "./policies/logPolicy";
import { proxyPolicy } from "./policies/proxyPolicy";
import { redirectPolicy, RedirectPolicyOptions } from "./policies/redirectPolicy";
import { RedirectPolicyOptions, redirectPolicy } from "./policies/redirectPolicy";
import { setClientRequestIdPolicy } from "./policies/setClientRequestIdPolicy";
import { tracingPolicy } from "./policies/tracingPolicy";
import { userAgentPolicy, UserAgentPolicyOptions } from "./policies/userAgentPolicy";
import { UserAgentPolicyOptions, userAgentPolicy } from "./policies/userAgentPolicy";
import { defaultRetryPolicy } from "./policies/defaultRetryPolicy";
import { isNode } from "./util/helpers";

Expand Down
4 changes: 2 additions & 2 deletions sdk/core/core-rest-pipeline/src/nodeHttpClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ import { Transform } from "stream";
import { AbortController, AbortError } from "@azure/abort-controller";
import {
HttpClient,
HttpHeaders,
PipelineRequest,
PipelineResponse,
TransferProgressEvent,
HttpHeaders,
RequestBodyType,
TransferProgressEvent,
} from "./interfaces";
import { createHttpHeaders } from "./httpHeaders";
import { RestError } from "./restError";
Expand Down
2 changes: 1 addition & 1 deletion sdk/core/core-rest-pipeline/src/pipeline.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

import { PipelineRequest, PipelineResponse, HttpClient, SendRequest } from "./interfaces";
import { HttpClient, PipelineRequest, PipelineResponse, SendRequest } from "./interfaces";

/**
* Policies are executed in phases.
Expand Down
10 changes: 5 additions & 5 deletions sdk/core/core-rest-pipeline/src/pipelineRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
// Licensed under the MIT license.

import {
PipelineRequest,
TransferProgressEvent,
RequestBodyType,
HttpMethods,
HttpHeaders,
FormDataMap,
HttpHeaders,
HttpMethods,
PipelineRequest,
ProxySettings,
RequestBodyType,
TransferProgressEvent,
} from "./interfaces";
import { createHttpHeaders } from "./httpHeaders";
import { AbortSignalLike } from "@azure/abort-controller";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

import { TokenCredential, GetTokenOptions, AccessToken } from "@azure/core-auth";
import { AccessToken, GetTokenOptions, TokenCredential } from "@azure/core-auth";
import { AzureLogger } from "@azure/logger";
import { PipelineResponse, PipelineRequest, SendRequest } from "../interfaces";
import { PipelineRequest, PipelineResponse, SendRequest } from "../interfaces";
import { PipelinePolicy } from "../pipeline";
import { createTokenCycler } from "../util/tokenCycler";

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

import { PipelineResponse, PipelineRequest, SendRequest } from "../interfaces";
import { PipelineRequest, PipelineResponse, SendRequest } from "../interfaces";
import { PipelinePolicy } from "../pipeline";

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

import { PipelineResponse, PipelineRequest, SendRequest } from "../interfaces";
import { PipelineRequest, PipelineResponse, SendRequest } from "../interfaces";
import { PipelinePolicy } from "../pipeline";

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// Licensed under the MIT license.

import FormData from "form-data";
import { PipelineResponse, PipelineRequest, SendRequest, FormDataMap } from "../interfaces";
import { FormDataMap, PipelineRequest, PipelineResponse, SendRequest } from "../interfaces";
import { PipelinePolicy } from "../pipeline";

/**
Expand Down
2 changes: 1 addition & 1 deletion sdk/core/core-rest-pipeline/src/policies/logPolicy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// Licensed under the MIT license.

import { Debugger } from "@azure/logger";
import { PipelineResponse, PipelineRequest, SendRequest } from "../interfaces";
import { PipelineRequest, PipelineResponse, SendRequest } from "../interfaces";
import { PipelinePolicy } from "../pipeline";
import { logger as coreLogger } from "../log";
import { Sanitizer } from "../util/sanitizer";
Expand Down
2 changes: 1 addition & 1 deletion sdk/core/core-rest-pipeline/src/policies/ndJsonPolicy.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

import { PipelineResponse, PipelineRequest, SendRequest } from "../interfaces";
import { PipelineRequest, PipelineResponse, SendRequest } from "../interfaces";
import { PipelinePolicy } from "../pipeline";

/**
Expand Down
6 changes: 3 additions & 3 deletions sdk/core/core-rest-pipeline/src/policies/proxyPolicy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ import * as https from "https";
import { HttpsProxyAgent, HttpsProxyAgentOptions } from "https-proxy-agent";
import { HttpProxyAgent, HttpProxyAgentOptions } from "http-proxy-agent";
import {
PipelineResponse,
HttpHeaders,
PipelineRequest,
SendRequest,
PipelineResponse,
ProxySettings,
HttpHeaders,
SendRequest,
} from "../interfaces";
import { PipelinePolicy } from "../pipeline";
import { URL } from "../util/url";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

import { PipelineResponse, PipelineRequest, SendRequest } from "../interfaces";
import { PipelineRequest, PipelineResponse, SendRequest } from "../interfaces";
import { PipelinePolicy } from "../pipeline";
import { URL } from "../util/url";

Expand Down
4 changes: 2 additions & 2 deletions sdk/core/core-rest-pipeline/src/policies/retryPolicy.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

import { PipelineResponse, PipelineRequest, SendRequest } from "../interfaces";
import { PipelineRequest, PipelineResponse, SendRequest } from "../interfaces";
import { PipelinePolicy } from "../pipeline";
import { delay } from "../util/helpers";
import { createClientLogger } from "@azure/logger";
Expand Down Expand Up @@ -113,7 +113,7 @@ export function retryPolicy(
strategyLogger.info(
`Retry ${retryCount}: Retry strategy ${strategy.name} retries after ${retryAfterInMs}`
);
await delay(retryAfterInMs);
await delay(retryAfterInMs, undefined, { abortSignal: request.abortSignal });
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It always feels cumbersome to have to pass in undefined in the middle. Can we make value (or valueToResolve) part of the options bag?

Copy link
Copy Markdown
Contributor Author

@HarshaNalluru HarshaNalluru Mar 11, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should I put everything in the options bag instead?
I feel the "value" is more important and it is better to not put it in the options bag.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

my impression is that only the first parameter is used most of time. But no strong feeling. This is not public.

continue retryRequest;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

import { PipelineResponse, PipelineRequest, SendRequest } from "../interfaces";
import { PipelineRequest, PipelineResponse, SendRequest } from "../interfaces";
import { PipelinePolicy } from "../pipeline";

/**
Expand Down
10 changes: 5 additions & 5 deletions sdk/core/core-rest-pipeline/src/policies/tracingPolicy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@
// Licensed under the MIT license.

import {
getTraceParentHeader,
createSpanFunction,
SpanStatusCode,
isSpanContextValid,
Span,
SpanOptions,
SpanStatusCode,
createSpanFunction,
getTraceParentHeader,
isSpanContextValid,
} from "@azure/core-tracing";
import { SpanKind } from "@azure/core-tracing";
import { PipelineResponse, PipelineRequest, SendRequest } from "../interfaces";
import { PipelineRequest, PipelineResponse, SendRequest } from "../interfaces";
import { PipelinePolicy } from "../pipeline";
import { getUserAgentValue } from "../util/userAgent";
import { logger } from "../log";
Expand Down
4 changes: 2 additions & 2 deletions sdk/core/core-rest-pipeline/src/policies/userAgentPolicy.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

import { PipelineResponse, PipelineRequest, SendRequest } from "../interfaces";
import { PipelineRequest, PipelineResponse, SendRequest } from "../interfaces";
import { PipelinePolicy } from "../pipeline";
import { getUserAgentValue, getUserAgentHeaderName } from "../util/userAgent";
import { getUserAgentHeaderName, getUserAgentValue } from "../util/userAgent";

const UserAgentHeaderName = getUserAgentHeaderName();

Expand Down
2 changes: 1 addition & 1 deletion sdk/core/core-rest-pipeline/src/restError.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

import { PipelineResponse, PipelineRequest } from "./interfaces";
import { PipelineRequest, PipelineResponse } from "./interfaces";
import { custom } from "./util/inspect";
import { Sanitizer } from "./util/sanitizer";

Expand Down
59 changes: 54 additions & 5 deletions sdk/core/core-rest-pipeline/src/util/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,71 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

import { AbortError, AbortSignalLike } from "@azure/abort-controller";

/**
* A constant that indicates whether the environment the code is running is Node.JS.
* @internal
*/
export const isNode =
typeof process !== "undefined" && Boolean(process.version) && Boolean(process.versions?.node);

const StandardAbortMessage = "The operation was aborted.";

/**
* A wrapper for setTimeout that resolves a promise after t milliseconds.
* @internal
* @param t - The number of milliseconds to be delayed.
* A wrapper for setTimeout that resolves a promise after delayInMs milliseconds.
* @param delayInMs - The number of milliseconds to be delayed.
* @param value - The value to be resolved with after a timeout of t milliseconds.
* @param options - The options for delay - currently abort options
* - abortSignal - The abortSignal associated with containing operation.
* - abortErrorMsg - The abort error message associated with containing operation.
* @returns Resolved promise
*/
export function delay<T>(t: number, value?: T): Promise<T | void> {
return new Promise((resolve) => setTimeout(() => resolve(value), t));
export function delay<T>(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don’t know if the delay function is the best place to put this code.
However, I understand why it would make sense to put it in the delay code.
So I think it’s ok!

Do we have other examples of the delay function handling the abort signal?
I wonder if this is going to be a pattern moving forward.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this delay function with abortSignal support be in core-rest-pipelines or perhaps in another core package?

Copy link
Copy Markdown
Contributor Author

@HarshaNalluru HarshaNalluru Mar 11, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have not written this from scratch, this was from core-http.

And we also have a similar(identical) method in core-amqp and service-bus also relies on it I think.

Basically, it's already prior art for a few years now and I'm not introducing any new patterns.

Copy link
Copy Markdown
Contributor Author

@HarshaNalluru HarshaNalluru Mar 11, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this delay function with abortSignal support be in core-rest-pipelines or perhaps in another core package?

@sadasant It is now added in core-rest-pipeline.
I would prefer core-util, but it is not a dependency of core-rest-pipeline.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Anyway, I like the design! A lot! Just food for thought.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ohh I understand now! Alright

delayInMs: number,
value?: T,
options?: {
abortSignal?: AbortSignalLike;
abortErrorMsg?: string;
}
): Promise<T | void> {
return new Promise((resolve, reject) => {
let timer: ReturnType<typeof setTimeout> | undefined = undefined;
let onAborted: (() => void) | undefined = undefined;

const rejectOnAbort = (): void => {
return reject(
new AbortError(options?.abortErrorMsg ? options?.abortErrorMsg : StandardAbortMessage)
);
};

const removeListeners = (): void => {
if (options?.abortSignal && onAborted) {
options.abortSignal.removeEventListener("abort", onAborted);
}
};

onAborted = (): void => {
if (timer) {
clearTimeout(timer);
}
removeListeners();
return rejectOnAbort();
};

if (options?.abortSignal && options.abortSignal.aborted) {
return rejectOnAbort();
}

timer = setTimeout(() => {
removeListeners();
resolve(value);
}, delayInMs);

if (options?.abortSignal) {
options.abortSignal.addEventListener("abort", onAborted);
}
});
}

/**
Expand Down
2 changes: 1 addition & 1 deletion sdk/core/core-rest-pipeline/src/util/sanitizer.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

import { isObject, UnknownObject } from "./helpers";
import { UnknownObject, isObject } from "./helpers";
import { URL } from "./url";

/**
Expand Down
2 changes: 1 addition & 1 deletion sdk/core/core-rest-pipeline/src/util/userAgent.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

import { setPlatformSpecificData, getHeaderName } from "./userAgentPlatform";
import { getHeaderName, setPlatformSpecificData } from "./userAgentPlatform";
import { SDK_VERSION } from "../constants";

function getUserAgentString(telemetryInfo: Map<string, string>): string {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@

import { assert } from "chai";
import * as sinon from "sinon";
import { TokenCredential, AccessToken } from "@azure/core-auth";
import { AccessToken, TokenCredential } from "@azure/core-auth";
import {
PipelinePolicy,
createPipelineRequest,
createHttpHeaders,
PipelineResponse,
bearerTokenAuthenticationPolicy,
SendRequest,
bearerTokenAuthenticationPolicy,
createHttpHeaders,
createPipelineRequest,
} from "../src";
import { DEFAULT_CYCLER_OPTIONS } from "../src/util/tokenCycler";

Expand Down
Loading