Skip to content

Commit ee4da29

Browse files
authored
Check both Content-MD5 and x-ms-blob-content-md5 (#2487)
1 parent 26de24b commit ee4da29

File tree

4 files changed

+71
-0
lines changed

4 files changed

+71
-0
lines changed

ChangeLog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ Blob:
1313
- GetBlob on Archive tier blobs now fails as expected.
1414
- Fixed issue of download 0 size blob with range > 0 should have header "Content-Range: bytes \*/0" in returned error. (issue #2458)
1515
- Aligned behavior with Azure to ignore invalid range requests for blob downloads. (issue #2458)
16+
- Consider both Content-MD5 and x-ms-blob-content-md5 when creating a blob.
1617

1718
Table:
1819

src/blob/handlers/BlockBlobHandler.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ export default class BlockBlobHandler
4646
context.request!.getHeader("content-type") ||
4747
"application/octet-stream";
4848
const contentMD5 = context.request!.getHeader("content-md5")
49+
|| context.request!.getHeader("x-ms-blob-content-md5")
4950
? options.blobHTTPHeaders.blobContentMD5 ||
5051
context.request!.getHeader("content-md5")
5152
: undefined;
@@ -182,6 +183,7 @@ export default class BlockBlobHandler
182183
// https://learn.microsoft.com/en-us/rest/api/storageservices/put-block
183184
// options.blobHTTPHeaders = options.blobHTTPHeaders || {};
184185
const contentMD5 = context.request!.getHeader("content-md5")
186+
|| context.request!.getHeader("x-ms-blob-content-md5")
185187
? options.transactionalContentMD5 ||
186188
context.request!.getHeader("content-md5")
187189
: undefined;
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { BaseRequestPolicy, WebResource } from "@azure/storage-blob";
2+
3+
// Create a policy factory with create() method provided
4+
// In TypeScript, following factory class needs to implement Azure.RequestPolicyFactory type
5+
export default class CustomHeaderPolicyFactory {
6+
// Constructor to accept parameters
7+
private key: string;
8+
private value: string;
9+
10+
constructor(key: string, value: string) {
11+
this.key = key;
12+
this.value = value;
13+
}
14+
15+
create(nextPolicy: any, options: any) {
16+
return new CustomHeaderPolicy(nextPolicy, options, this.key, this.value);
17+
}
18+
}
19+
20+
// Create a policy by extending from Azure.BaseRequestPolicy
21+
class CustomHeaderPolicy extends BaseRequestPolicy {
22+
private key: string;
23+
private value: string;
24+
25+
constructor(nextPolicy: any, options: any, key: string, value: string) {
26+
super(nextPolicy, options);
27+
this.key = key;
28+
this.value = value;
29+
}
30+
31+
// Customize HTTP requests and responses by overriding sendRequest
32+
// Parameter request is Azure.WebResource type
33+
async sendRequest(request: WebResource) {
34+
request.headers.set(this.key, this.value);
35+
36+
return await this._nextPolicy.sendRequest(request);
37+
}
38+
}

tests/blob/apis/blob.test.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
getUniqueName,
1919
sleep
2020
} from "../../testutils";
21+
import CustomHeaderPolicyFactory from "../RequestPolicy/CustomHeaderPolicyFactory";
2122
import RangePolicyFactory from "../RequestPolicy/RangePolicyFactory";
2223

2324
// Set true to enable debug log
@@ -2527,6 +2528,35 @@ describe("BlobAPIs", () => {
25272528
assert.deepStrictEqual(result, tags);
25282529
});
25292530

2531+
it("upload invalid x-ms-blob-content-md5 @loki @sql", async () => {
2532+
const pipeline = newPipeline(
2533+
new StorageSharedKeyCredential(
2534+
EMULATOR_ACCOUNT_NAME,
2535+
EMULATOR_ACCOUNT_KEY
2536+
),
2537+
{
2538+
retryOptions: { maxTries: 1 },
2539+
// Make sure socket is closed once the operation is done.
2540+
keepAliveOptions: { enable: false }
2541+
}
2542+
);
2543+
pipeline.factories.unshift(
2544+
new CustomHeaderPolicyFactory("x-ms-blob-content-md5", "invalid-md5")
2545+
);
2546+
const serviceClient = new BlobServiceClient(baseURL, pipeline);
2547+
const containerClient = serviceClient.getContainerClient(containerName);
2548+
2549+
const blobClient = containerClient.getBlockBlobClient(blobName);
2550+
try {
2551+
await blobClient.upload("hello", 5, { tier: "Hot" });
2552+
assert.fail("Expected MD5 error");
2553+
} catch (err) {
2554+
assert.deepStrictEqual((err as any).statusCode, 400);
2555+
assert.deepStrictEqual((err as any).code, 'InvalidOperation');
2556+
assert.deepStrictEqual((err as any).details.errorCode, 'InvalidOperation');
2557+
}
2558+
});
2559+
25302560
it("Acquire Lease on Breaking Lease status, if LeaseId not match, throw LeaseIdMismatchWithLease error @loki @sql", async () => {
25312561
// TODO: implement the case later
25322562
});

0 commit comments

Comments
 (0)