Skip to content

Commit

Permalink
feat(client-s3): use regional endpoint by default and support aws-glo…
Browse files Browse the repository at this point in the history
…bal region (#1552)

* feat(client-s3): use regional endpoint by default

* feat(client-s3): use global endpoint if region is set to aws-global

Co-authored-by: Trivikram Kamat <16024985+trivikr@users.noreply.github.com>
  • Loading branch information
AllanZhengYP and trivikr committed Oct 7, 2020
1 parent 0439946 commit 47c6599
Show file tree
Hide file tree
Showing 7 changed files with 116 additions and 3 deletions.
3 changes: 2 additions & 1 deletion clients/client-s3/S3Client.ts
Expand Up @@ -240,7 +240,7 @@ import {
} from "@aws-sdk/middleware-host-header";
import { getLoggerPlugin } from "@aws-sdk/middleware-logger";
import { RetryInputConfig, RetryResolvedConfig, getRetryPlugin, resolveRetryConfig } from "@aws-sdk/middleware-retry";
import { getValidateBucketNamePlugin } from "@aws-sdk/middleware-sdk-s3";
import { getUseRegionalEndpointPlugin, getValidateBucketNamePlugin } from "@aws-sdk/middleware-sdk-s3";
import {
AwsAuthInputConfig,
AwsAuthResolvedConfig,
Expand Down Expand Up @@ -621,6 +621,7 @@ export class S3Client extends __Client<
this.middlewareStack.use(getUserAgentPlugin(this.config));
this.middlewareStack.use(getContentLengthPlugin(this.config));
this.middlewareStack.use(getValidateBucketNamePlugin(this.config));
this.middlewareStack.use(getUseRegionalEndpointPlugin(this.config));
this.middlewareStack.use(getAddExpectContinuePlugin(this.config));
this.middlewareStack.use(getHostHeaderPlugin(this.config));
this.middlewareStack.use(getLoggerPlugin(this.config));
Expand Down
Expand Up @@ -100,7 +100,12 @@ public List<RuntimeClientPlugin> getClientPlugins() {
.servicePredicate((m, s) -> testServiceId(s, "API Gateway"))
.build(),
RuntimeClientPlugin.builder()
.withConventions(AwsDependency.VALIDATE_BUCKET_NAME.dependency, "ValidateBucketName",
.withConventions(AwsDependency.S3_MIDDLEWARE.dependency, "ValidateBucketName",
HAS_MIDDLEWARE)
.servicePredicate((m, s) -> testServiceId(s, "S3"))
.build(),
RuntimeClientPlugin.builder()
.withConventions(AwsDependency.S3_MIDDLEWARE.dependency, "UseRegionalEndpoint",
HAS_MIDDLEWARE)
.servicePredicate((m, s) -> testServiceId(s, "S3"))
.build(),
Expand Down
Expand Up @@ -32,7 +32,7 @@ public enum AwsDependency implements SymbolDependencyContainer {
MIDDLEWARE_SIGNING(NORMAL_DEPENDENCY, "@aws-sdk/middleware-signing", "^1.0.0-beta.1"),
CREDENTIAL_PROVIDER_NODE(NORMAL_DEPENDENCY, "@aws-sdk/credential-provider-node", "^1.0.0-beta.1"),
ACCEPT_HEADER(NORMAL_DEPENDENCY, "@aws-sdk/middleware-sdk-api-gateway", "^1.0.0-beta.1"),
VALIDATE_BUCKET_NAME(NORMAL_DEPENDENCY, "@aws-sdk/middleware-sdk-s3", "^1.0.0-beta.1"),
S3_MIDDLEWARE(NORMAL_DEPENDENCY, "@aws-sdk/middleware-sdk-s3", "^1.0.0-beta.1"),
ADD_EXPECT_CONTINUE(NORMAL_DEPENDENCY, "@aws-sdk/middleware-expect-continue", "^1.0.0-beta.1"),
GLACIER_MIDDLEWARE(NORMAL_DEPENDENCY, "@aws-sdk/middleware-sdk-glacier", "^1.0.0-beta.1"),
MACHINELEARNING_MIDDLEWARE(NORMAL_DEPENDENCY, "@aws-sdk/middleware-sdk-machinelearning", "^1.0.0-beta.1"),
Expand Down
1 change: 1 addition & 0 deletions packages/middleware-sdk-s3/package.json
Expand Up @@ -19,6 +19,7 @@
"license": "Apache-2.0",
"dependencies": {
"@aws-sdk/util-arn-parser": "1.0.0-gamma.3",
"@aws-sdk/protocol-http": "1.0.0-gamma.7",
"tslib": "^1.8.0"
},
"devDependencies": {
Expand Down
1 change: 1 addition & 0 deletions packages/middleware-sdk-s3/src/index.ts
@@ -1 +1,2 @@
export * from "./validate-bucket-name";
export * from "./use-regional-endpoint";
62 changes: 62 additions & 0 deletions packages/middleware-sdk-s3/src/use-regional-endpoint.spec.ts
@@ -0,0 +1,62 @@
import { HttpRequest } from "@aws-sdk/protocol-http";

import { useRegionalEndpointMiddleware } from "./use-regional-endpoint";

describe("useRegionalEndpointMiddleware", () => {
const mockNextHandler = jest.fn();
beforeEach(() => {
jest.clearAllMocks();
});

it("should accept any endpoint if set by customer", async () => {
const config = { isCustomEndpoint: true, region: async () => "foo-region" };
const handler = useRegionalEndpointMiddleware(config)(mockNextHandler, {} as any);
await handler({
input: {},
request: new HttpRequest({
hostname: "s3.us-east-1.amazonaws.com",
}),
});
expect(mockNextHandler.mock.calls.length).toBe(1);
expect(mockNextHandler.mock.calls[0][0].request.hostname).toEqual("s3.us-east-1.amazonaws.com");
});

it("should modify the hostname if it's global endpoint", async () => {
const config = { isCustomEndpoint: false, region: async () => "foo-region" };
const handler = useRegionalEndpointMiddleware(config)(mockNextHandler, {} as any);
await handler({
input: {},
request: new HttpRequest({
hostname: "s3.amazonaws.com",
}),
});
expect(mockNextHandler.mock.calls.length).toBe(1);
expect(mockNextHandler.mock.calls[0][0].request.hostname).toEqual("s3.us-east-1.amazonaws.com");
});

it("should not modify the hostname if it's regional endpoint", async () => {
const config = { isCustomEndpoint: false, region: async () => "foo-region" };
const handler = useRegionalEndpointMiddleware(config)(mockNextHandler, {} as any);
await handler({
input: {},
request: new HttpRequest({
hostname: "s3.us-west-2.amazonaws.com",
}),
});
expect(mockNextHandler.mock.calls.length).toBe(1);
expect(mockNextHandler.mock.calls[0][0].request.hostname).toEqual("s3.us-west-2.amazonaws.com");
});

it("should use global endpoint if region is set to 'aws-global'", async () => {
const config = { isCustomEndpoint: false, region: async () => "aws-global" };
const handler = useRegionalEndpointMiddleware(config)(mockNextHandler, {} as any);
await handler({
input: {},
request: new HttpRequest({
hostname: "s3.aws-global.amazonaws.com",
}),
});
expect(mockNextHandler.mock.calls.length).toBe(1);
expect(mockNextHandler.mock.calls[0][0].request.hostname).toEqual("s3.amazonaws.com");
});
});
43 changes: 43 additions & 0 deletions packages/middleware-sdk-s3/src/use-regional-endpoint.ts
@@ -0,0 +1,43 @@
import { HttpRequest } from "@aws-sdk/protocol-http";
import {
BuildHandler,
BuildHandlerArguments,
BuildHandlerOptions,
BuildHandlerOutput,
BuildMiddleware,
MetadataBearer,
Pluggable,
Provider,
} from "@aws-sdk/types";

type PreviouslyResolved = {
region: Provider<string>;
isCustomEndpoint: boolean;
};

export const useRegionalEndpointMiddleware = (config: PreviouslyResolved): BuildMiddleware<any, any> => <
Output extends MetadataBearer
>(
next: BuildHandler<any, Output>
): BuildHandler<any, Output> => async (args: BuildHandlerArguments<any>): Promise<BuildHandlerOutput<Output>> => {
const { request } = args;
if (!HttpRequest.isInstance(request) || config.isCustomEndpoint) return next({ ...args });
if (request.hostname === "s3.amazonaws.com") {
request.hostname = "s3.us-east-1.amazonaws.com";
} else if ("aws-global" === (await config.region())) {
request.hostname = "s3.amazonaws.com";
}
return next({ ...args });
};

export const useRegionalEndpointMiddlewareOptions: BuildHandlerOptions = {
step: "build",
tags: ["USE_REGIONAL_ENDPOINT", "S3"],
name: "useRegionalEndpointMiddleware",
};

export const getUseRegionalEndpointPlugin = (config: PreviouslyResolved): Pluggable<any, any> => ({
applyToStack: (clientStack) => {
clientStack.add(useRegionalEndpointMiddleware(config), useRegionalEndpointMiddlewareOptions);
},
});

0 comments on commit 47c6599

Please sign in to comment.