diff --git a/clients/client-connectparticipant/ConnectParticipantClient.ts b/clients/client-connectparticipant/ConnectParticipantClient.ts index 898d4a5be8c8..a5fc6f0d1db2 100644 --- a/clients/client-connectparticipant/ConnectParticipantClient.ts +++ b/clients/client-connectparticipant/ConnectParticipantClient.ts @@ -26,7 +26,13 @@ import { resolveHostHeaderConfig, } from "@aws-sdk/middleware-host-header"; import { getLoggerPlugin } from "@aws-sdk/middleware-logger"; -import { RetryInputConfig, RetryResolvedConfig, getRetryPlugin, resolveRetryConfig } from "@aws-sdk/middleware-retry"; +import { + RetryInputConfig, + RetryResolvedConfig, + getOmitRetryHeadersPlugin, + getRetryPlugin, + resolveRetryConfig, +} from "@aws-sdk/middleware-retry"; import { AwsAuthInputConfig, AwsAuthResolvedConfig, @@ -223,6 +229,7 @@ export class ConnectParticipantClient extends __Client< this.middlewareStack.use(getHostHeaderPlugin(this.config)); this.middlewareStack.use(getLoggerPlugin(this.config)); this.middlewareStack.use(getUserAgentPlugin(this.config)); + this.middlewareStack.use(getOmitRetryHeadersPlugin(this.config)); } destroy(): void { diff --git a/clients/client-iot-data-plane/IoTDataPlaneClient.ts b/clients/client-iot-data-plane/IoTDataPlaneClient.ts index d367ad632c80..11f9140422b4 100644 --- a/clients/client-iot-data-plane/IoTDataPlaneClient.ts +++ b/clients/client-iot-data-plane/IoTDataPlaneClient.ts @@ -23,7 +23,13 @@ import { resolveHostHeaderConfig, } from "@aws-sdk/middleware-host-header"; import { getLoggerPlugin } from "@aws-sdk/middleware-logger"; -import { RetryInputConfig, RetryResolvedConfig, getRetryPlugin, resolveRetryConfig } from "@aws-sdk/middleware-retry"; +import { + RetryInputConfig, + RetryResolvedConfig, + getOmitRetryHeadersPlugin, + getRetryPlugin, + resolveRetryConfig, +} from "@aws-sdk/middleware-retry"; import { AwsAuthInputConfig, AwsAuthResolvedConfig, @@ -225,6 +231,7 @@ export class IoTDataPlaneClient extends __Client< this.middlewareStack.use(getHostHeaderPlugin(this.config)); this.middlewareStack.use(getLoggerPlugin(this.config)); this.middlewareStack.use(getUserAgentPlugin(this.config)); + this.middlewareStack.use(getOmitRetryHeadersPlugin(this.config)); } destroy(): void { diff --git a/clients/client-iot/IoTClient.ts b/clients/client-iot/IoTClient.ts index 95f504178176..6ea6ac6c2243 100644 --- a/clients/client-iot/IoTClient.ts +++ b/clients/client-iot/IoTClient.ts @@ -596,7 +596,13 @@ import { resolveHostHeaderConfig, } from "@aws-sdk/middleware-host-header"; import { getLoggerPlugin } from "@aws-sdk/middleware-logger"; -import { RetryInputConfig, RetryResolvedConfig, getRetryPlugin, resolveRetryConfig } from "@aws-sdk/middleware-retry"; +import { + RetryInputConfig, + RetryResolvedConfig, + getOmitRetryHeadersPlugin, + getRetryPlugin, + resolveRetryConfig, +} from "@aws-sdk/middleware-retry"; import { AwsAuthInputConfig, AwsAuthResolvedConfig, @@ -1217,6 +1223,7 @@ export class IoTClient extends __Client< this.middlewareStack.use(getHostHeaderPlugin(this.config)); this.middlewareStack.use(getLoggerPlugin(this.config)); this.middlewareStack.use(getUserAgentPlugin(this.config)); + this.middlewareStack.use(getOmitRetryHeadersPlugin(this.config)); } destroy(): void { diff --git a/clients/client-pinpoint-sms-voice/PinpointSMSVoiceClient.ts b/clients/client-pinpoint-sms-voice/PinpointSMSVoiceClient.ts index 1c49e2d8ef48..b0327c87da78 100644 --- a/clients/client-pinpoint-sms-voice/PinpointSMSVoiceClient.ts +++ b/clients/client-pinpoint-sms-voice/PinpointSMSVoiceClient.ts @@ -44,7 +44,13 @@ import { resolveHostHeaderConfig, } from "@aws-sdk/middleware-host-header"; import { getLoggerPlugin } from "@aws-sdk/middleware-logger"; -import { RetryInputConfig, RetryResolvedConfig, getRetryPlugin, resolveRetryConfig } from "@aws-sdk/middleware-retry"; +import { + RetryInputConfig, + RetryResolvedConfig, + getOmitRetryHeadersPlugin, + getRetryPlugin, + resolveRetryConfig, +} from "@aws-sdk/middleware-retry"; import { AwsAuthInputConfig, AwsAuthResolvedConfig, @@ -242,6 +248,7 @@ export class PinpointSMSVoiceClient extends __Client< this.middlewareStack.use(getHostHeaderPlugin(this.config)); this.middlewareStack.use(getLoggerPlugin(this.config)); this.middlewareStack.use(getUserAgentPlugin(this.config)); + this.middlewareStack.use(getOmitRetryHeadersPlugin(this.config)); } destroy(): void { diff --git a/clients/client-qldb/QLDBClient.ts b/clients/client-qldb/QLDBClient.ts index 34f9d4ebeccb..965d4e91179d 100644 --- a/clients/client-qldb/QLDBClient.ts +++ b/clients/client-qldb/QLDBClient.ts @@ -58,7 +58,13 @@ import { resolveHostHeaderConfig, } from "@aws-sdk/middleware-host-header"; import { getLoggerPlugin } from "@aws-sdk/middleware-logger"; -import { RetryInputConfig, RetryResolvedConfig, getRetryPlugin, resolveRetryConfig } from "@aws-sdk/middleware-retry"; +import { + RetryInputConfig, + RetryResolvedConfig, + getOmitRetryHeadersPlugin, + getRetryPlugin, + resolveRetryConfig, +} from "@aws-sdk/middleware-retry"; import { AwsAuthInputConfig, AwsAuthResolvedConfig, @@ -278,6 +284,7 @@ export class QLDBClient extends __Client< this.middlewareStack.use(getHostHeaderPlugin(this.config)); this.middlewareStack.use(getLoggerPlugin(this.config)); this.middlewareStack.use(getUserAgentPlugin(this.config)); + this.middlewareStack.use(getOmitRetryHeadersPlugin(this.config)); } destroy(): void { diff --git a/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AddOmitRetryHeadersDependency.java b/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AddOmitRetryHeadersDependency.java new file mode 100644 index 000000000000..7fd794263b5e --- /dev/null +++ b/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AddOmitRetryHeadersDependency.java @@ -0,0 +1,53 @@ +/* + * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package software.amazon.smithy.aws.typescript.codegen; + +import static software.amazon.smithy.typescript.codegen.integration.RuntimeClientPlugin.Convention.HAS_MIDDLEWARE; + +import java.util.List; +import java.util.Set; + +import software.amazon.smithy.aws.traits.ServiceTrait; +import software.amazon.smithy.typescript.codegen.TypeScriptDependency; +import software.amazon.smithy.typescript.codegen.integration.RuntimeClientPlugin; +import software.amazon.smithy.typescript.codegen.integration.TypeScriptIntegration; +import software.amazon.smithy.utils.ListUtils; +import software.amazon.smithy.utils.SetUtils; + + +public class AddOmitRetryHeadersDependency implements TypeScriptIntegration { + private static final Set SERVICE_IDS = SetUtils.of( + "ConnectParticipant", + "IoT Data Plane", + "IoT", + "Pinpoint SMS Voice", + "QLDB" + ); + + @Override + public List getClientPlugins() { + return ListUtils.of( + RuntimeClientPlugin.builder() + .withConventions(TypeScriptDependency.MIDDLEWARE_RETRY.dependency, "OmitRetryHeaders", + HAS_MIDDLEWARE) + .servicePredicate((m, s) -> { + String sdkId = s.getTrait(ServiceTrait.class).map(ServiceTrait::getSdkId).orElse(""); + return SERVICE_IDS.contains(sdkId); + }) + .build() + ); + } +} diff --git a/codegen/smithy-aws-typescript-codegen/src/main/resources/META-INF/services/software.amazon.smithy.typescript.codegen.integration.TypeScriptIntegration b/codegen/smithy-aws-typescript-codegen/src/main/resources/META-INF/services/software.amazon.smithy.typescript.codegen.integration.TypeScriptIntegration index cc49c3d7e7e9..2f54cf2e5d48 100644 --- a/codegen/smithy-aws-typescript-codegen/src/main/resources/META-INF/services/software.amazon.smithy.typescript.codegen.integration.TypeScriptIntegration +++ b/codegen/smithy-aws-typescript-codegen/src/main/resources/META-INF/services/software.amazon.smithy.typescript.codegen.integration.TypeScriptIntegration @@ -13,3 +13,4 @@ software.amazon.smithy.aws.typescript.codegen.AddEventStreamHandlingDependency software.amazon.smithy.aws.typescript.codegen.AddHttp2Dependency software.amazon.smithy.aws.typescript.codegen.AddTranscribeStreamingDependency software.amazon.smithy.aws.typescript.codegen.AddUserAgentDependency +software.amazon.smithy.aws.typescript.codegen.AddOmitRetryHeadersDependency diff --git a/packages/middleware-retry/src/constants.ts b/packages/middleware-retry/src/constants.ts index c14baa7b87c3..2df12ce6fa9e 100644 --- a/packages/middleware-retry/src/constants.ts +++ b/packages/middleware-retry/src/constants.ts @@ -37,3 +37,13 @@ export const TIMEOUT_RETRY_COST = 10; * if an SDK operation invocation succeeds without requiring a retry request. */ export const NO_RETRY_INCREMENT = 1; + +/** + * Header name for SDK invocation ID + */ +export const INVOCATION_ID_HEADER = "amz-sdk-invocation-id"; + +/** + * Header name for request retry information. + */ +export const REQUEST_HEADER = "amz-sdk-request"; diff --git a/packages/middleware-retry/src/defaultStrategy.ts b/packages/middleware-retry/src/defaultStrategy.ts index 473812a800f2..5557d32538b3 100644 --- a/packages/middleware-retry/src/defaultStrategy.ts +++ b/packages/middleware-retry/src/defaultStrategy.ts @@ -4,7 +4,13 @@ import { SdkError } from "@aws-sdk/smithy-client"; import { FinalizeHandler, FinalizeHandlerArguments, MetadataBearer, Provider, RetryStrategy } from "@aws-sdk/types"; import { v4 } from "uuid"; -import { DEFAULT_RETRY_DELAY_BASE, INITIAL_RETRY_TOKENS, THROTTLING_RETRY_DELAY_BASE } from "./constants"; +import { + DEFAULT_RETRY_DELAY_BASE, + INITIAL_RETRY_TOKENS, + INVOCATION_ID_HEADER, + REQUEST_HEADER, + THROTTLING_RETRY_DELAY_BASE, +} from "./constants"; import { getDefaultRetryQuota } from "./defaultRetryQuota"; import { defaultDelayDecider } from "./delayDecider"; import { defaultRetryDecider } from "./retryDecider"; @@ -108,13 +114,13 @@ export class StandardRetryStrategy implements RetryStrategy { const { request } = args; if (HttpRequest.isInstance(request)) { - request.headers["amz-sdk-invocation-id"] = v4(); + request.headers[INVOCATION_ID_HEADER] = v4(); } while (true) { try { if (HttpRequest.isInstance(request)) { - request.headers["amz-sdk-request"] = `attempt=${attempts + 1}; max=${maxAttempts}`; + request.headers[REQUEST_HEADER] = `attempt=${attempts + 1}; max=${maxAttempts}`; } const { response, output } = await next(args); diff --git a/packages/middleware-retry/src/index.ts b/packages/middleware-retry/src/index.ts index 29758eac24be..929d7e1237fa 100644 --- a/packages/middleware-retry/src/index.ts +++ b/packages/middleware-retry/src/index.ts @@ -1,4 +1,5 @@ export * from "./retryMiddleware"; +export * from "./omitRetryHeadersMiddleware"; export * from "./defaultStrategy"; export * from "./configurations"; export * from "./delayDecider"; diff --git a/packages/middleware-retry/src/omitRetryHeadersMiddleware.spec.ts b/packages/middleware-retry/src/omitRetryHeadersMiddleware.spec.ts new file mode 100644 index 000000000000..cf439b6fdd7a --- /dev/null +++ b/packages/middleware-retry/src/omitRetryHeadersMiddleware.spec.ts @@ -0,0 +1,49 @@ +import { HttpRequest } from "@aws-sdk/protocol-http"; +import { FinalizeHandlerArguments, MiddlewareStack } from "@aws-sdk/types"; + +import { INVOCATION_ID_HEADER, REQUEST_HEADER } from "./constants"; +import { + getOmitRetryHeadersPlugin, + omitRetryHeadersMiddleware, + omitRetryHeadersMiddlewareOptions, +} from "./omitRetryHeadersMiddleware"; + +describe("getOmitRetryHeadersPlugin", () => { + const mockClientStack = { + add: jest.fn(), + addRelativeTo: jest.fn(), + }; + + afterEach(() => { + jest.clearAllMocks(); + }); + + it(`adds omitRetryHeadersMiddleware`, () => { + getOmitRetryHeadersPlugin({}).applyToStack((mockClientStack as unknown) as MiddlewareStack); + expect(mockClientStack.addRelativeTo).toHaveBeenCalledTimes(1); + expect(mockClientStack.addRelativeTo.mock.calls[0][1]).toEqual(omitRetryHeadersMiddlewareOptions); + }); +}); + +describe("omitRetryHeadersMiddleware", () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + it("remove retry headers", async () => { + const next = jest.fn(); + const args = { + request: new HttpRequest({ + headers: { + [INVOCATION_ID_HEADER]: "12345", + [REQUEST_HEADER]: "maxAttempts=30", + }, + }), + }; + + await omitRetryHeadersMiddleware()(next)(args as FinalizeHandlerArguments); + expect(next).toHaveBeenCalledTimes(1); + expect(next.mock.calls[0][0].request.headers[INVOCATION_ID_HEADER]).toBeUndefined(); + expect(next.mock.calls[0][0].request.headers[REQUEST_HEADER]).toBeUndefined(); + }); +}); diff --git a/packages/middleware-retry/src/omitRetryHeadersMiddleware.ts b/packages/middleware-retry/src/omitRetryHeadersMiddleware.ts new file mode 100644 index 000000000000..5fcfbf3fb991 --- /dev/null +++ b/packages/middleware-retry/src/omitRetryHeadersMiddleware.ts @@ -0,0 +1,37 @@ +import { HttpRequest } from "@aws-sdk/protocol-http"; +import { + FinalizeHandler, + FinalizeHandlerArguments, + FinalizeHandlerOutput, + MetadataBearer, + Pluggable, + RelativeMiddlewareOptions, +} from "@aws-sdk/types"; + +import { INVOCATION_ID_HEADER, REQUEST_HEADER } from "./constants"; + +export const omitRetryHeadersMiddleware = () => ( + next: FinalizeHandler +): FinalizeHandler => async ( + args: FinalizeHandlerArguments +): Promise> => { + const { request } = args; + if (HttpRequest.isInstance(request)) { + delete request.headers[INVOCATION_ID_HEADER]; + delete request.headers[REQUEST_HEADER]; + } + return next(args); +}; + +export const omitRetryHeadersMiddlewareOptions: RelativeMiddlewareOptions = { + name: "omitRetryHeadersMiddleware", + tags: ["RETRY", "HEADERS", "OMIT_RETRY_HEADERS"], + relation: "before", + toMiddleware: "awsAuthMiddleware", +}; + +export const getOmitRetryHeadersPlugin = (options: unknown): Pluggable => ({ + applyToStack: (clientStack) => { + clientStack.addRelativeTo(omitRetryHeadersMiddleware(), omitRetryHeadersMiddlewareOptions); + }, +});