Skip to content

Commit

Permalink
chore(middleware-user-agent): add feature detection for account-id, s…
Browse files Browse the repository at this point in the history
…3-express, lib-dynamodb (#6541)
  • Loading branch information
kuhe authored Oct 4, 2024
1 parent 544120e commit dfda129
Show file tree
Hide file tree
Showing 12 changed files with 144 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { setFeature } from "@aws-sdk/core";
import { Command as $Command } from "@smithy/smithy-client";
import {
DeserializeHandler,
Expand Down Expand Up @@ -51,6 +52,7 @@ export abstract class DynamoDBDocumentClientCommand<
async (
args: InitializeHandlerArguments<Input | BaseInput>
): Promise<InitializeHandlerOutput<Output | BaseOutput>> => {
setFeature(context, "DDB_MAPPER", "d");
args.input = marshallInput(this.input, this.inputKeyNodes, marshallOptions);
context.dynamoDbDocumentClientOptions =
context.dynamoDbDocumentClientOptions || DynamoDBDocumentClientCommand.defaultLogFilterOverrides;
Expand Down
1 change: 1 addition & 0 deletions packages/middleware-flexible-checksums/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"dependencies": {
"@aws-crypto/crc32": "5.2.0",
"@aws-crypto/crc32c": "5.2.0",
"@aws-sdk/core": "*",
"@aws-sdk/types": "*",
"@smithy/is-array-buffer": "^3.0.0",
"@smithy/node-config-provider": "^3.1.8",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { setFeature } from "@aws-sdk/core";
import { HttpRequest } from "@smithy/protocol-http";
import {
BuildHandler,
Expand All @@ -10,6 +11,7 @@ import {
} from "@smithy/types";

import { PreviouslyResolved } from "./configuration";
import { ChecksumAlgorithm } from "./constants";
import { getChecksumAlgorithmForRequest } from "./getChecksumAlgorithmForRequest";
import { getChecksumLocationName } from "./getChecksumLocationName";
import { hasHeader } from "./hasHeader";
Expand Down Expand Up @@ -72,6 +74,20 @@ export const flexibleChecksumsMiddleware =
let updatedHeaders = headers;

if (checksumAlgorithm) {
switch (checksumAlgorithm) {
case ChecksumAlgorithm.CRC32:
setFeature(context, "FLEXIBLE_CHECKSUMS_REQ_CRC32", "U");
break;
case ChecksumAlgorithm.CRC32C:
setFeature(context, "FLEXIBLE_CHECKSUMS_REQ_CRC32C", "V");
break;
case ChecksumAlgorithm.SHA1:
setFeature(context, "FLEXIBLE_CHECKSUMS_REQ_SHA1", "X");
break;
case ChecksumAlgorithm.SHA256:
setFeature(context, "FLEXIBLE_CHECKSUMS_REQ_SHA256", "Y");
break;
}
const checksumLocationName = getChecksumLocationName(checksumAlgorithm);
const checksumAlgorithmFn = selectChecksumAlgorithmFunction(checksumAlgorithm, config);
if (isStreaming(requestBody)) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { S3 } from "@aws-sdk/client-s3";
import { ChecksumAlgorithm, S3 } from "@aws-sdk/client-s3";
import { Transform } from "stream";

import { requireRequestsFrom } from "../../../private/aws-util-test/src";
Expand Down Expand Up @@ -124,5 +124,33 @@ describe("middleware-flexible-checksums", () => {

expect.hasAssertions();
});

describe("features", () => {
[
["SHA256", "Y"],
["SHA1", "X"],
["CRC32", "U"],
["CRC32C", "V"],
].forEach(([algo, id]) => {
it(`should feature-detect checksum ${algo}=${id}`, async () => {
const client = new S3({ region: "us-west-2", logger });

requireRequestsFrom(client).toMatch({
headers: {
"user-agent": new RegExp(`(.*?) m\/${id}$`),
},
});

await client.putObject({
Bucket: "b",
Key: "k",
Body: "abcd",
ChecksumAlgorithm: algo as ChecksumAlgorithm,
});

expect.hasAssertions();
});
});
});
});
});
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { setFeature } from "@aws-sdk/core";
import { AwsCredentialIdentity } from "@aws-sdk/types";
import { HttpRequest } from "@smithy/protocol-http";
import {
Expand Down Expand Up @@ -51,6 +52,7 @@ export const s3ExpressMiddleware: (options: S3ExpressResolvedConfig) => BuildMid
endpoint.properties?.bucketType === S3_EXPRESS_BUCKET_TYPE;

if (isS3ExpressBucket) {
setFeature(context, "S3_EXPRESS_BUCKET", "J");
context.isS3ExpressBucket = true;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,5 +84,22 @@ describe("middleware-s3-express", () => {

expect.hasAssertions();
});

it("should feature-detect S3 express bucket", async () => {
const client = new S3({
region: "us-west-2",
s3ExpressIdentityProvider,
});

requireRequestsFrom(client).toMatch({
headers: {
"user-agent": /(.*?) m\/J$/,
},
});

await client.headBucket({
Bucket: "aws-sdk-js-v3-test--usw2-az1--x-s3",
});
});
});
});
1 change: 1 addition & 0 deletions packages/middleware-user-agent/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
},
"license": "Apache-2.0",
"dependencies": {
"@aws-sdk/core": "*",
"@aws-sdk/types": "*",
"@aws-sdk/util-endpoints": "*",
"@smithy/core": "^2.4.8",
Expand Down
39 changes: 39 additions & 0 deletions packages/middleware-user-agent/src/check-features.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { setFeature } from "@aws-sdk/core";
import type { AccountIdEndpointMode } from "@aws-sdk/core/account-id-endpoint";
import type { AwsHandlerExecutionContext } from "@aws-sdk/types";
import type { IHttpRequest } from "@smithy/protocol-http";
import type { BuildHandlerArguments, Provider } from "@smithy/types";

/**
* @internal
*/
type PreviouslyResolved = Partial<{
accountIdEndpointMode?: Provider<AccountIdEndpointMode>;
}>;

/**
* @internal
* Check for features that don't have a middleware activation site but
* may be detected on the context, client config, or request.
*/
export async function checkFeatures(
context: AwsHandlerExecutionContext,
config: PreviouslyResolved,
args: BuildHandlerArguments<any>
): Promise<void> {
// eslint-disable-next-line
const request = args.request as IHttpRequest;
if (typeof config.accountIdEndpointMode === "function") {
switch (await config.accountIdEndpointMode?.()) {
case "disabled":
setFeature(context, "ACCOUNT_ID_MODE_DISABLED", "Q");
break;
case "preferred":
setFeature(context, "ACCOUNT_ID_MODE_PREFERRED", "P");
break;
case "required":
setFeature(context, "ACCOUNT_ID_MODE_REQUIRED", "R");
break;
}
}
}
2 changes: 1 addition & 1 deletion packages/middleware-user-agent/src/configurations.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Logger, Provider, UserAgent } from "@smithy/types";
import { normalizeProvider } from "@smithy/core";
import { Logger, Provider, UserAgent } from "@smithy/types";

/**
* @internal
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { CodeCatalyst } from "@aws-sdk/client-codecatalyst";
import { DynamoDB } from "@aws-sdk/client-dynamodb";
import { S3 } from "@aws-sdk/client-s3";
import { DynamoDBDocument } from "@aws-sdk/lib-dynamodb";

import { requireRequestsFrom } from "../../../private/aws-util-test/src";

Expand All @@ -23,4 +26,32 @@ describe("middleware-user-agent", () => {
});
});
});

describe("features", () => {
it("should detect DDB mapper, and account id mode", async () => {
const client = new DynamoDB({
credentials: {
accessKeyId: "",
secretAccessKey: "",
accountId: "123",
},
accountIdEndpointMode: async () => "preferred" as const,
});

const doc = DynamoDBDocument.from(client);

requireRequestsFrom(doc).toMatch({
headers: {
"user-agent": /(.*?) m\/d,P$/,
},
});

await doc.get({
TableName: "table",
Key: {
id: "1",
},
});
});
});
});
4 changes: 4 additions & 0 deletions packages/middleware-user-agent/src/user-agent-middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
UserAgentPair,
} from "@smithy/types";

import { checkFeatures } from "./check-features";
import { UserAgentResolvedConfig } from "./configurations";
import {
SPACE,
Expand Down Expand Up @@ -51,12 +52,15 @@ export const userAgentMiddleware =
const { headers } = request;
const userAgent = context?.userAgent?.map(escapeUserAgent) || [];
const defaultUserAgent = (await options.defaultUserAgentProvider()).map(escapeUserAgent);

await checkFeatures(context, options as any, args);
const awsContext = context as AwsHandlerExecutionContext;
defaultUserAgent.push(
`m/${encodeFeatures(
Object.assign({}, context.__smithy_context?.features, awsContext.__aws_sdk_context?.features)
)}`
);

const customUserAgent = options?.customUserAgent?.map(escapeUserAgent) || [];
const appId = await options.userAgentAppId();
if (appId) {
Expand Down
2 changes: 1 addition & 1 deletion private/aws-util-test/src/requests/test-http-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export class TestHttpHandler implements HttpHandler {
*/
public watch(client: Client<any, any, any>, matcher: HttpRequestMatcher = this.matcher) {
this.client = client;
this.originalRequestHandler = client.config.originalRequestHandler;
this.originalRequestHandler = client.config.requestHandler;
// mock credentials to avoid default chain lookup.
client.config.credentials = async () => MOCK_CREDENTIALS;
client.config.credentialDefaultProvider = () => {
Expand Down

0 comments on commit dfda129

Please sign in to comment.