diff --git a/lib/lib-dynamodb/src/baseCommand/DynamoDBDocumentClientCommand.ts b/lib/lib-dynamodb/src/baseCommand/DynamoDBDocumentClientCommand.ts
index 94cf791d1488..2ccf963df43d 100644
--- a/lib/lib-dynamodb/src/baseCommand/DynamoDBDocumentClientCommand.ts
+++ b/lib/lib-dynamodb/src/baseCommand/DynamoDBDocumentClientCommand.ts
@@ -1,3 +1,4 @@
+import { setFeature } from "@aws-sdk/core";
import { Command as $Command } from "@smithy/smithy-client";
import {
DeserializeHandler,
@@ -51,6 +52,7 @@ export abstract class DynamoDBDocumentClientCommand<
async (
args: InitializeHandlerArguments
): Promise> => {
+ setFeature(context, "DDB_MAPPER", "d");
args.input = marshallInput(this.input, this.inputKeyNodes, marshallOptions);
context.dynamoDbDocumentClientOptions =
context.dynamoDbDocumentClientOptions || DynamoDBDocumentClientCommand.defaultLogFilterOverrides;
diff --git a/packages/middleware-flexible-checksums/package.json b/packages/middleware-flexible-checksums/package.json
index c01828292d46..69dfa2c37a69 100644
--- a/packages/middleware-flexible-checksums/package.json
+++ b/packages/middleware-flexible-checksums/package.json
@@ -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",
diff --git a/packages/middleware-flexible-checksums/src/flexibleChecksumsMiddleware.ts b/packages/middleware-flexible-checksums/src/flexibleChecksumsMiddleware.ts
index 5f426f35050c..cc5efb32212a 100644
--- a/packages/middleware-flexible-checksums/src/flexibleChecksumsMiddleware.ts
+++ b/packages/middleware-flexible-checksums/src/flexibleChecksumsMiddleware.ts
@@ -1,3 +1,4 @@
+import { setFeature } from "@aws-sdk/core";
import { HttpRequest } from "@smithy/protocol-http";
import {
BuildHandler,
@@ -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";
@@ -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)) {
diff --git a/packages/middleware-flexible-checksums/src/middleware-flexible-checksums.integ.spec.ts b/packages/middleware-flexible-checksums/src/middleware-flexible-checksums.integ.spec.ts
index f65c31caf8cb..027e8ad74b1f 100644
--- a/packages/middleware-flexible-checksums/src/middleware-flexible-checksums.integ.spec.ts
+++ b/packages/middleware-flexible-checksums/src/middleware-flexible-checksums.integ.spec.ts
@@ -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";
@@ -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();
+ });
+ });
+ });
});
});
diff --git a/packages/middleware-sdk-s3/src/s3-express/functions/s3ExpressMiddleware.ts b/packages/middleware-sdk-s3/src/s3-express/functions/s3ExpressMiddleware.ts
index 57fa6e1b9d25..32ab90d3d738 100644
--- a/packages/middleware-sdk-s3/src/s3-express/functions/s3ExpressMiddleware.ts
+++ b/packages/middleware-sdk-s3/src/s3-express/functions/s3ExpressMiddleware.ts
@@ -1,3 +1,4 @@
+import { setFeature } from "@aws-sdk/core";
import { AwsCredentialIdentity } from "@aws-sdk/types";
import { HttpRequest } from "@smithy/protocol-http";
import {
@@ -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;
}
diff --git a/packages/middleware-sdk-s3/src/s3-express/middleware-s3-express.integ.spec.ts b/packages/middleware-sdk-s3/src/s3-express/middleware-s3-express.integ.spec.ts
index d99fb1b37f24..a73facfe5d8c 100644
--- a/packages/middleware-sdk-s3/src/s3-express/middleware-s3-express.integ.spec.ts
+++ b/packages/middleware-sdk-s3/src/s3-express/middleware-s3-express.integ.spec.ts
@@ -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",
+ });
+ });
});
});
diff --git a/packages/middleware-user-agent/package.json b/packages/middleware-user-agent/package.json
index 496ebecd6241..f44013005ed4 100644
--- a/packages/middleware-user-agent/package.json
+++ b/packages/middleware-user-agent/package.json
@@ -22,6 +22,7 @@
},
"license": "Apache-2.0",
"dependencies": {
+ "@aws-sdk/core": "*",
"@aws-sdk/types": "*",
"@aws-sdk/util-endpoints": "*",
"@smithy/core": "^2.4.8",
diff --git a/packages/middleware-user-agent/src/check-features.ts b/packages/middleware-user-agent/src/check-features.ts
new file mode 100644
index 000000000000..c47446aba3cd
--- /dev/null
+++ b/packages/middleware-user-agent/src/check-features.ts
@@ -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;
+}>;
+
+/**
+ * @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
+): Promise {
+ // 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;
+ }
+ }
+}
diff --git a/packages/middleware-user-agent/src/configurations.ts b/packages/middleware-user-agent/src/configurations.ts
index e778e81779d3..af973ba931d5 100644
--- a/packages/middleware-user-agent/src/configurations.ts
+++ b/packages/middleware-user-agent/src/configurations.ts
@@ -1,5 +1,5 @@
-import { Logger, Provider, UserAgent } from "@smithy/types";
import { normalizeProvider } from "@smithy/core";
+import { Logger, Provider, UserAgent } from "@smithy/types";
/**
* @internal
diff --git a/packages/middleware-user-agent/src/middleware-user-agent.integ.spec.ts b/packages/middleware-user-agent/src/middleware-user-agent.integ.spec.ts
index aa2c67fef18c..4b8370263826 100644
--- a/packages/middleware-user-agent/src/middleware-user-agent.integ.spec.ts
+++ b/packages/middleware-user-agent/src/middleware-user-agent.integ.spec.ts
@@ -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";
@@ -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",
+ },
+ });
+ });
+ });
});
diff --git a/packages/middleware-user-agent/src/user-agent-middleware.ts b/packages/middleware-user-agent/src/user-agent-middleware.ts
index 260151a13cb5..4438ede668d0 100644
--- a/packages/middleware-user-agent/src/user-agent-middleware.ts
+++ b/packages/middleware-user-agent/src/user-agent-middleware.ts
@@ -13,6 +13,7 @@ import {
UserAgentPair,
} from "@smithy/types";
+import { checkFeatures } from "./check-features";
import { UserAgentResolvedConfig } from "./configurations";
import {
SPACE,
@@ -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) {
diff --git a/private/aws-util-test/src/requests/test-http-handler.ts b/private/aws-util-test/src/requests/test-http-handler.ts
index 8187924f1fd3..0be329ed7ddb 100644
--- a/private/aws-util-test/src/requests/test-http-handler.ts
+++ b/private/aws-util-test/src/requests/test-http-handler.ts
@@ -56,7 +56,7 @@ export class TestHttpHandler implements HttpHandler {
*/
public watch(client: Client, 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 = () => {