Skip to content

Commit d150104

Browse files
authored
test(middleware-flexible-checksums): update instructions for MD5 fallback (#7002)
* chore(middleware-flexible-checksums): update instructions for MD5 fallback * test(middleware-flexible-checksums): rm context.step reference * test(middleware-flexible-checksums): rm logging
1 parent bc0ac06 commit d150104

File tree

2 files changed

+90
-81
lines changed

2 files changed

+90
-81
lines changed

packages/middleware-flexible-checksums/src/middleware-md5-fallback.e2e.spec.ts

+48-40
Original file line numberDiff line numberDiff line change
@@ -81,47 +81,56 @@ describe("S3 MD5 Fallback for DeleteObjects", () => {
8181
let md5Added = false;
8282
let crc32Removed = false;
8383

84-
md5S3Client.middlewareStack.add(
84+
const md5Middleware =
8585
(next: FinalizeHandler<ServiceInputTypes, ServiceOutputTypes>, context: HandlerExecutionContext) =>
86-
async (
87-
args: FinalizeHandlerArguments<ServiceInputTypes>
88-
): Promise<FinalizeHandlerOutput<ServiceOutputTypes>> => {
89-
const request = args.request as HttpRequest;
90-
const isDeleteObjects = context.commandName === "DeleteObjectsCommand";
91-
92-
if (!isDeleteObjects) {
93-
return next(args);
86+
async (args: FinalizeHandlerArguments<ServiceInputTypes>): Promise<FinalizeHandlerOutput<ServiceOutputTypes>> => {
87+
const request = args.request as HttpRequest;
88+
const isDeleteObjects = context.commandName === "DeleteObjectsCommand";
89+
90+
if (!isDeleteObjects) {
91+
return next(args);
92+
}
93+
94+
const headers = request.headers;
95+
96+
// Log headers *before* modification
97+
// console.log(`[${context.commandName}] Headers before MD5 middleware:`, JSON.stringify(headers, null, 2));
98+
99+
// Remove checksum headers
100+
Object.keys(headers).forEach((header) => {
101+
const lowerHeader = header.toLowerCase();
102+
if (lowerHeader.startsWith("x-amz-checksum-") || lowerHeader.startsWith("x-amz-sdk-checksum-")) {
103+
// console.log(`[${context.commandName}] Removing header: ${header}`);
104+
delete headers[header];
105+
crc32Removed = true;
94106
}
95-
96-
const result = await next(args);
97-
const headers = request.headers;
98-
99-
// Remove checksum headers
100-
Object.keys(headers).forEach((header) => {
101-
if (
102-
header.toLowerCase().startsWith("x-amz-checksum-") ||
103-
header.toLowerCase().startsWith("x-amz-sdk-checksum-")
104-
) {
105-
delete headers[header];
106-
crc32Removed = true;
107-
}
108-
});
109-
110-
// Add MD5
111-
if (request.body) {
112-
const bodyContent = Buffer.from(request.body);
113-
const md5Hash = createHash("md5").update(bodyContent).digest("base64");
114-
headers["Content-MD5"] = md5Hash;
115-
md5Added = true;
116-
}
117-
118-
return result;
119-
},
120-
{
121-
step: "finalizeRequest",
122-
name: "addMD5Checksum",
123-
}
124-
);
107+
});
108+
109+
// Add MD5
110+
if (request.body) {
111+
const bodyContent = Buffer.from(request.body);
112+
const md5Hash = createHash("md5").update(bodyContent).digest("base64");
113+
headers["Content-MD5"] = md5Hash;
114+
// console.log(`[${context.commandName}] Added Content-MD5: ${md5Hash}`);
115+
md5Added = true;
116+
}
117+
118+
// Log headers *after* modification
119+
// console.log(`[${context.commandName}] Headers after MD5 middleware:`, JSON.stringify(headers, null, 2));
120+
121+
return await next(args);
122+
};
123+
124+
// Add the middleware relative to the flexible checksums middleware
125+
md5S3Client.middlewareStack.addRelativeTo(md5Middleware, {
126+
relation: "after",
127+
toMiddleware: "flexibleChecksumsMiddleware",
128+
name: "addMD5Checksum",
129+
tags: ["MD5_CHECKSUM"],
130+
});
131+
132+
// Log the entire middleware stack for debugging if needed
133+
// console.log("Middleware Stack:", md5S3Client.middlewareStack.identify());
125134

126135
const response = await md5S3Client.send(
127136
new DeleteObjectsCommand({
@@ -132,7 +141,6 @@ describe("S3 MD5 Fallback for DeleteObjects", () => {
132141
})
133142
);
134143

135-
// If MD5 wasn't properly set, this call will fail
136144
expect(response.Deleted?.length).toBe(1);
137145
expect(md5Added).toBe(true);
138146
expect(crc32Removed).toBe(true);

supplemental-docs/MD5_FALLBACK.md

+42-41
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ In AWS SDK for JavaScript [v3.729.0](https://github.com/aws/aws-sdk-js-v3/releas
66
shipped a feature that [changed default object integrity in S3](https://github.com/aws/aws-sdk-js-v3/issues/6810).
77
The SDKs now default to using more modern checksums (like CRC32) to ensure object integrity, whereas
88
previously MD5 checksums were being used. Some third-party S3-compatible services currently do not
9-
support these checksums and require MD5 checksums. Furthermore, the [DeleteObjects operation in S3
10-
requires a Content-MD5 request header](https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteObjects.html)
11-
in which case too, you should use the MD5 fallback as noted below.
9+
support these checksums. To our knowledge, [`DeleteObjects` operation in S3
10+
is the only affected operation](https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteObjects.html).
11+
1212
If you wish to fallback to the old behavior of sending MD5 checksums, for operations like
1313
`DeleteObjectsCommand` this is how you can do it in AWS SDK for JavaScript v3:
1414

@@ -27,43 +27,44 @@ import { createHash } from "crypto";
2727
*/
2828
export function createS3ClientWithMD5() {
2929
const client = new S3Client({});
30-
const md5Hash = createHash("md5");
3130

32-
client.middlewareStack.add(
33-
(next, context) => async (args) => {
34-
// Check if this is a DeleteObjects command
35-
const isDeleteObjects = context.commandName === "DeleteObjectsCommand";
31+
// Define the middleware function
32+
const md5Middleware = (next, context) => async (args) => {
33+
// Check if this is a DeleteObjects command
34+
const isDeleteObjects = context.commandName === "DeleteObjectsCommand";
3635

37-
if (!isDeleteObjects) {
38-
return next(args);
39-
}
36+
if (!isDeleteObjects) {
37+
return next(args);
38+
}
4039

41-
const result = await next(args);
42-
const headers = args.request.headers;
43-
44-
// Remove any checksum headers
45-
Object.keys(headers).forEach((header) => {
46-
if (
47-
header.toLowerCase().startsWith("x-amz-checksum-") ||
48-
header.toLowerCase().startsWith("x-amz-sdk-checksum-")
49-
) {
50-
delete headers[header];
51-
}
52-
});
53-
54-
// Add MD5
55-
if (args.request.body) {
56-
const bodyContent = Buffer.from(args.request.body);
57-
headers["Content-MD5"] = md5Hash.update(bodyContent).digest("base64");
40+
const headers = args.request.headers;
41+
42+
// Remove any checksum headers added by default middleware
43+
// This ensures our Content-MD5 is the primary integrity check
44+
Object.keys(headers).forEach((header) => {
45+
const lowerHeader = header.toLowerCase();
46+
if (lowerHeader.startsWith("x-amz-checksum-") || lowerHeader.startsWith("x-amz-sdk-checksum-")) {
47+
delete headers[header];
5848
}
49+
});
5950

60-
return result;
61-
},
62-
{
63-
step: "finalizeRequest",
64-
name: "addMD5Checksum",
51+
// Add Content-MD5 header
52+
if (args.request.body) {
53+
const bodyContent = Buffer.from(args.request.body);
54+
headers["Content-MD5"] = createHash("md5").update(bodyContent).digest("base64");
6555
}
66-
);
56+
57+
return await next(args);
58+
};
59+
60+
// Add the middleware relative to the flexible checksums middleware
61+
// This ensures it runs after default checksums might be added, but before signing
62+
client.middlewareStack.addRelativeTo(md5Middleware, {
63+
relation: "after",
64+
toMiddleware: "flexibleChecksumsMiddleware",
65+
name: "addMD5ChecksumForDeleteObjects", // Optional: Name it whatever you'd like
66+
tags: ["MD5_FALLBACK"],
67+
});
6768

6869
return client;
6970
}
@@ -98,15 +99,15 @@ try {
9899

99100
## How It Works
100101

101-
The solution adds middleware to the S3 client that:
102+
The solution adds middleware to the S3 client's stack using `addRelativeTo`. This ensures the custom middleware executes at the correct point in the request lifecycle:
102103

103-
1. Detects DeleteObjects operations using the command name
104-
2. Lets the SDK add its default headers
105-
3. Removes any checksum headers in the finalizeRequest step
106-
4. Calculates an MD5 hash of the request body
107-
5. Adds the MD5 hash as a Content-MD5 header
104+
1. Detects `DeleteObjects` operations using the command name.
105+
2. It's placed **after** the SDK's default `flexibleChecksumsMiddleware`. This allows the default middleware to potentially add its checksum headers first (usually in the `build` step).
106+
3. The custom middleware then **removes** any `x-amz-checksum-*` or `x-amz-sdk-checksum-*` headers that might have been added.
107+
4. It calculates an MD5 hash of the request body.
108+
5. It adds the MD5 hash as a `Content-MD5` header.
108109

109-
This sequence ensures that we properly replace the checksums with MD5 checksum.
110+
This sequence ensures that we reliably replace the default checksums with `Content-MD5` specifically for `DeleteObjects` operations before the request is signed and sent.
110111

111112
## Usage Notes
112113

0 commit comments

Comments
 (0)