Skip to content

Commit

Permalink
fix(client-s3-control): add prefix dedupe middleware
Browse files Browse the repository at this point in the history
  • Loading branch information
kuhe committed Dec 14, 2022
1 parent 0cba099 commit bb3c464
Show file tree
Hide file tree
Showing 16 changed files with 403 additions and 248 deletions.
2 changes: 2 additions & 0 deletions clients/client-s3-control/src/S3ControlClient.ts
Expand Up @@ -12,6 +12,7 @@ import { getLoggerPlugin } from "@aws-sdk/middleware-logger";
import { getRecursionDetectionPlugin } from "@aws-sdk/middleware-recursion-detection";
import { getRetryPlugin, resolveRetryConfig, RetryInputConfig, RetryResolvedConfig } from "@aws-sdk/middleware-retry";
import {
getHostPrefixDeduplicationPlugin,
resolveS3ControlConfig,
S3ControlInputConfig,
S3ControlResolvedConfig,
Expand Down Expand Up @@ -569,6 +570,7 @@ export class S3ControlClient extends __Client<
this.middlewareStack.use(getLoggerPlugin(this.config));
this.middlewareStack.use(getRecursionDetectionPlugin(this.config));
this.middlewareStack.use(getAwsAuthPlugin(this.config));
this.middlewareStack.use(getHostPrefixDeduplicationPlugin(this.config));
this.middlewareStack.use(getUserAgentPlugin(this.config));
}

Expand Down
1 change: 1 addition & 0 deletions clients/client-s3/src/commands/CreateBucketCommand.ts
Expand Up @@ -202,6 +202,7 @@ export class CreateBucketCommand extends $Command<

public static getEndpointParameterInstructions(): EndpointParameterInstructions {
return {
DisableAccessPoints: { type: "staticContextParams", value: true },
Bucket: { type: "contextParams", name: "Bucket" },
ForcePathStyle: { type: "clientContextParams", name: "forcePathStyle" },
UseArnRegion: { type: "clientContextParams", name: "useArnRegion" },
Expand Down
Expand Up @@ -82,6 +82,7 @@ export class WriteGetObjectResponseCommand extends $Command<

public static getEndpointParameterInstructions(): EndpointParameterInstructions {
return {
UseObjectLambdaEndpoint: { type: "staticContextParams", value: true },
ForcePathStyle: { type: "clientContextParams", name: "forcePathStyle" },
UseArnRegion: { type: "clientContextParams", name: "useArnRegion" },
DisableMultiRegionAccessPoints: { type: "clientContextParams", name: "disableMultiregionAccessPoints" },
Expand Down
Expand Up @@ -54,6 +54,11 @@ public List<RuntimeClientPlugin> getClientPlugins() {
.withConventions(AwsDependency.S3_CONTROL_MIDDLEWARE.dependency, "S3Control", HAS_CONFIG)
.servicePredicate((m, s) -> isS3Control(s))
.build(),
RuntimeClientPlugin.builder()
.withConventions(AwsDependency.S3_CONTROL_MIDDLEWARE.dependency,
"HostPrefixDeduplication", HAS_MIDDLEWARE)
.servicePredicate((m, s) -> isS3Control(s))
.build(),
RuntimeClientPlugin.builder()
.withConventions(
AwsDependency.S3_CONTROL_MIDDLEWARE.dependency,
Expand Down
@@ -0,0 +1,18 @@
import { deduplicateHostPrefix } from "./deduplicateHostPrefix";

describe(deduplicateHostPrefix.name, () => {
it("should deduplicate host name prefixes", () => {
expect(deduplicateHostPrefix("a.a.host.com")).toEqual("a.host.com");
expect(deduplicateHostPrefix("1234567890.1234567890.host.com")).toEqual("1234567890.host.com");
expect(deduplicateHostPrefix("abcdefgh.abcdefgh.host.com")).toEqual("abcdefgh.host.com");
}),
it("should do nothing if no duplication exists in the first two positions", () => {
expect(deduplicateHostPrefix("b.a.host.com")).toEqual("b.a.host.com");
expect(deduplicateHostPrefix("0123456789.1234567890.host.com")).toEqual("0123456789.1234567890.host.com");
expect(deduplicateHostPrefix("zabcdefg.abcdefgh.host.com")).toEqual("zabcdefg.abcdefgh.host.com");

expect(deduplicateHostPrefix("12345.abcdefgh.12345.12345.host.com")).toEqual(
"12345.abcdefgh.12345.12345.host.com"
);
});
});
@@ -0,0 +1,11 @@
/**
* @example
* 12345.12345.____.com should become 12345.____.com.
*/
export const deduplicateHostPrefix = (hostname: string): string => {
const [prefix1, prefix2, ...rest] = hostname.split(".");
if (prefix1 === prefix2) {
return [prefix1, ...rest].join(".");
}
return hostname;
};
@@ -0,0 +1,52 @@
import {
EndpointV2,
HandlerExecutionContext,
Pluggable,
RelativeMiddlewareOptions,
SerializeHandler,
SerializeHandlerArguments,
SerializeHandlerOutput,
SerializeMiddleware,
} from "@aws-sdk/types";

import { deduplicateHostPrefix } from "./deduplicateHostPrefix";

/**
* @internal
* This customization handles an edge case where
* a hostprefix may be duplicated in the endpoint ruleset resolution
* and hostPrefix serialization via the pre-endpoints 2.0 trait,
* and which cannot be reconciled automatically.
*/
export const hostPrefixDeduplicationMiddleware = (): SerializeMiddleware<any, any> => {
return (next: SerializeHandler<any, any>, context: HandlerExecutionContext): SerializeHandler<any, any> =>
async (args: SerializeHandlerArguments<any>): Promise<SerializeHandlerOutput<any>> => {
const endpoint: EndpointV2 | undefined = context.endpointV2;
if (endpoint?.url?.hostname) {
endpoint.url.hostname = deduplicateHostPrefix(endpoint.url.hostname);
} else {
throw new Error("Endpoint w/ url.hostname not found in hostPrefixDeduplicationMiddleware.");
}
return next(args);
};
};

/**
* @internal
*/
export const hostPrefixDeduplicationMiddlewareOptions: RelativeMiddlewareOptions = {
tags: ["HOST_PREFIX_DEDUPLICATION", "ENDPOINT_V2", "ENDPOINT"],
toMiddleware: "endpointV2Middleware",
relation: "after",
name: "hostPrefixDeduplicationMiddleware",
override: true,
};

/**
* @internal
*/
export const getHostPrefixDeduplicationPlugin = <T>(config?: T): Pluggable<any, any> => ({
applyToStack: (clientStack) => {
clientStack.add(hostPrefixDeduplicationMiddleware(), hostPrefixDeduplicationMiddlewareOptions);
},
});
1 change: 1 addition & 0 deletions packages/middleware-sdk-s3-control/src/index.ts
Expand Up @@ -6,4 +6,5 @@ export {
updateArnablesRequestMiddlewareOptions,
getProcessArnablesPlugin,
} from "./process-arnables-plugin";
export * from "./host-prefix-deduplication/hostPrefixDeduplicationMiddleware";
export * from "./redirect-from-postid";
48 changes: 28 additions & 20 deletions private/aws-protocoltests-ec2/test/functional/ec2query.spec.ts
Expand Up @@ -154,6 +154,14 @@ const clientParams = {
credentials: { accessKeyId: "key", secretAccessKey: "secret" },
};

/**
* A wrapper function that shadows `fail` from jest-jasmine2
* (jasmine2 was replaced with circus in > v27 as the default test runner)
*/
const fail = (error?: any): never => {
throw new Error(error);
};

/**
* Empty input serializes no extra query params
*/
Expand Down Expand Up @@ -214,7 +222,7 @@ it("Ec2QueryEmptyInputAndEmptyOutput:Response", async () => {
try {
r = await client.send(command);
} catch (err) {
fail("Expected a valid response to be returned, got err.");
fail("Expected a valid response to be returned, got " + err);
return;
}
expect(r["$metadata"].httpStatusCode).toBe(200);
Expand Down Expand Up @@ -322,7 +330,7 @@ it("Ec2GreetingWithErrors:Response", async () => {
try {
r = await client.send(command);
} catch (err) {
fail("Expected a valid response to be returned, got err.");
fail("Expected a valid response to be returned, got " + err);
return;
}
expect(r["$metadata"].httpStatusCode).toBe(200);
Expand Down Expand Up @@ -505,7 +513,7 @@ it("Ec2IgnoresWrappingXmlName:Response", async () => {
try {
r = await client.send(command);
} catch (err) {
fail("Expected a valid response to be returned, got err.");
fail("Expected a valid response to be returned, got " + err);
return;
}
expect(r["$metadata"].httpStatusCode).toBe(200);
Expand Down Expand Up @@ -625,7 +633,7 @@ it("Ec2QueryNoInputAndOutput:Response", async () => {
try {
r = await client.send(command);
} catch (err) {
fail("Expected a valid response to be returned, got err.");
fail("Expected a valid response to be returned, got " + err);
return;
}
expect(r["$metadata"].httpStatusCode).toBe(200);
Expand Down Expand Up @@ -976,7 +984,7 @@ it("Ec2RecursiveShapes:Response", async () => {
try {
r = await client.send(command);
} catch (err) {
fail("Expected a valid response to be returned, got err.");
fail("Expected a valid response to be returned, got " + err);
return;
}
expect(r["$metadata"].httpStatusCode).toBe(200);
Expand Down Expand Up @@ -1532,7 +1540,7 @@ it("Ec2SimpleScalarProperties:Response", async () => {
try {
r = await client.send(command);
} catch (err) {
fail("Expected a valid response to be returned, got err.");
fail("Expected a valid response to be returned, got " + err);
return;
}
expect(r["$metadata"].httpStatusCode).toBe(200);
Expand Down Expand Up @@ -1592,7 +1600,7 @@ it("Ec2QuerySupportsNaNFloatOutputs:Response", async () => {
try {
r = await client.send(command);
} catch (err) {
fail("Expected a valid response to be returned, got err.");
fail("Expected a valid response to be returned, got " + err);
return;
}
expect(r["$metadata"].httpStatusCode).toBe(200);
Expand Down Expand Up @@ -1636,7 +1644,7 @@ it("Ec2QuerySupportsInfinityFloatOutputs:Response", async () => {
try {
r = await client.send(command);
} catch (err) {
fail("Expected a valid response to be returned, got err.");
fail("Expected a valid response to be returned, got " + err);
return;
}
expect(r["$metadata"].httpStatusCode).toBe(200);
Expand Down Expand Up @@ -1680,7 +1688,7 @@ it("Ec2QuerySupportsNegativeInfinityFloatOutputs:Response", async () => {
try {
r = await client.send(command);
} catch (err) {
fail("Expected a valid response to be returned, got err.");
fail("Expected a valid response to be returned, got " + err);
return;
}
expect(r["$metadata"].httpStatusCode).toBe(200);
Expand Down Expand Up @@ -1724,7 +1732,7 @@ it("Ec2XmlBlobs:Response", async () => {
try {
r = await client.send(command);
} catch (err) {
fail("Expected a valid response to be returned, got err.");
fail("Expected a valid response to be returned, got " + err);
return;
}
expect(r["$metadata"].httpStatusCode).toBe(200);
Expand Down Expand Up @@ -1766,7 +1774,7 @@ it("Ec2XmlEmptyBlobs:Response", async () => {
try {
r = await client.send(command);
} catch (err) {
fail("Expected a valid response to be returned, got err.");
fail("Expected a valid response to be returned, got " + err);
return;
}
expect(r["$metadata"].httpStatusCode).toBe(200);
Expand Down Expand Up @@ -1808,7 +1816,7 @@ it("Ec2XmlEmptySelfClosedBlobs:Response", async () => {
try {
r = await client.send(command);
} catch (err) {
fail("Expected a valid response to be returned, got err.");
fail("Expected a valid response to be returned, got " + err);
return;
}
expect(r["$metadata"].httpStatusCode).toBe(200);
Expand Down Expand Up @@ -1850,7 +1858,7 @@ it("Ec2XmlEmptyLists:Response", async () => {
try {
r = await client.send(command);
} catch (err) {
fail("Expected a valid response to be returned, got err.");
fail("Expected a valid response to be returned, got " + err);
return;
}
expect(r["$metadata"].httpStatusCode).toBe(200);
Expand Down Expand Up @@ -1914,7 +1922,7 @@ it("Ec2XmlEnums:Response", async () => {
try {
r = await client.send(command);
} catch (err) {
fail("Expected a valid response to be returned, got err.");
fail("Expected a valid response to be returned, got " + err);
return;
}
expect(r["$metadata"].httpStatusCode).toBe(200);
Expand Down Expand Up @@ -2025,7 +2033,7 @@ it("Ec2XmlLists:Response", async () => {
try {
r = await client.send(command);
} catch (err) {
fail("Expected a valid response to be returned, got err.");
fail("Expected a valid response to be returned, got " + err);
return;
}
expect(r["$metadata"].httpStatusCode).toBe(200);
Expand Down Expand Up @@ -2117,7 +2125,7 @@ it("Ec2XmlNamespaces:Response", async () => {
try {
r = await client.send(command);
} catch (err) {
fail("Expected a valid response to be returned, got err.");
fail("Expected a valid response to be returned, got " + err);
return;
}
expect(r["$metadata"].httpStatusCode).toBe(200);
Expand Down Expand Up @@ -2163,7 +2171,7 @@ it("Ec2XmlTimestamps:Response", async () => {
try {
r = await client.send(command);
} catch (err) {
fail("Expected a valid response to be returned, got err.");
fail("Expected a valid response to be returned, got " + err);
return;
}
expect(r["$metadata"].httpStatusCode).toBe(200);
Expand Down Expand Up @@ -2205,7 +2213,7 @@ it("Ec2XmlTimestampsWithDateTimeFormat:Response", async () => {
try {
r = await client.send(command);
} catch (err) {
fail("Expected a valid response to be returned, got err.");
fail("Expected a valid response to be returned, got " + err);
return;
}
expect(r["$metadata"].httpStatusCode).toBe(200);
Expand Down Expand Up @@ -2247,7 +2255,7 @@ it("Ec2XmlTimestampsWithEpochSecondsFormat:Response", async () => {
try {
r = await client.send(command);
} catch (err) {
fail("Expected a valid response to be returned, got err.");
fail("Expected a valid response to be returned, got " + err);
return;
}
expect(r["$metadata"].httpStatusCode).toBe(200);
Expand Down Expand Up @@ -2289,7 +2297,7 @@ it("Ec2XmlTimestampsWithHttpDateFormat:Response", async () => {
try {
r = await client.send(command);
} catch (err) {
fail("Expected a valid response to be returned, got err.");
fail("Expected a valid response to be returned, got " + err);
return;
}
expect(r["$metadata"].httpStatusCode).toBe(200);
Expand Down

0 comments on commit bb3c464

Please sign in to comment.