Skip to content

Commit

Permalink
Check both Content-MD5 and x-ms-blob-content-md5
Browse files Browse the repository at this point in the history
  • Loading branch information
gaul committed Jan 14, 2025
1 parent 26de24b commit b98dcaa
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 0 deletions.
1 change: 1 addition & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Blob:
- GetBlob on Archive tier blobs now fails as expected.
- Fixed issue of download 0 size blob with range > 0 should have header "Content-Range: bytes \*/0" in returned error. (issue #2458)
- Aligned behavior with Azure to ignore invalid range requests for blob downloads. (issue #2458)
- Consider both Content-MD5 and x-ms-blob-content-md5 when creating a blob.

Table:

Expand Down
2 changes: 2 additions & 0 deletions src/blob/handlers/BlockBlobHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export default class BlockBlobHandler
context.request!.getHeader("content-type") ||
"application/octet-stream";
const contentMD5 = context.request!.getHeader("content-md5")
|| context.request!.getHeader("x-ms-blob-content-md5")
? options.blobHTTPHeaders.blobContentMD5 ||
context.request!.getHeader("content-md5")
: undefined;
Expand Down Expand Up @@ -182,6 +183,7 @@ export default class BlockBlobHandler
// https://learn.microsoft.com/en-us/rest/api/storageservices/put-block
// options.blobHTTPHeaders = options.blobHTTPHeaders || {};
const contentMD5 = context.request!.getHeader("content-md5")
|| context.request!.getHeader("x-ms-blob-content-md5")
? options.transactionalContentMD5 ||
context.request!.getHeader("content-md5")
: undefined;
Expand Down
38 changes: 38 additions & 0 deletions tests/blob/RequestPolicy/CustomHeaderPolicyFactory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { BaseRequestPolicy, WebResource } from "@azure/storage-blob";

// Create a policy factory with create() method provided
// In TypeScript, following factory class needs to implement Azure.RequestPolicyFactory type
export default class CustomHeaderPolicyFactory {
// Constructor to accept parameters
private key: string;
private value: string;

constructor(key: string, value: string) {
this.key = key;
this.value = value;
}

create(nextPolicy: any, options: any) {
return new CustomHeaderPolicy(nextPolicy, options, this.key, this.value);
}
}

// Create a policy by extending from Azure.BaseRequestPolicy
class CustomHeaderPolicy extends BaseRequestPolicy {
private key: string;
private value: string;

constructor(nextPolicy: any, options: any, key: string, value: string) {
super(nextPolicy, options);
this.key = key;
this.value = value;
}

// Customize HTTP requests and responses by overriding sendRequest
// Parameter request is Azure.WebResource type
async sendRequest(request: WebResource) {
request.headers.set(this.key, this.value);

return await this._nextPolicy.sendRequest(request);
}
}
30 changes: 30 additions & 0 deletions tests/blob/apis/blob.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
getUniqueName,
sleep
} from "../../testutils";
import CustomHeaderPolicyFactory from "../RequestPolicy/CustomHeaderPolicyFactory";
import RangePolicyFactory from "../RequestPolicy/RangePolicyFactory";

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

it("upload invalid x-ms-blob-content-md5 @loki @sql", async () => {
const pipeline = newPipeline(
new StorageSharedKeyCredential(
EMULATOR_ACCOUNT_NAME,
EMULATOR_ACCOUNT_KEY
),
{
retryOptions: { maxTries: 1 },
// Make sure socket is closed once the operation is done.
keepAliveOptions: { enable: false }
}
);
pipeline.factories.unshift(
new CustomHeaderPolicyFactory("x-ms-blob-content-md5", "invalid-md5")
);
const serviceClient = new BlobServiceClient(baseURL, pipeline);
const containerClient = serviceClient.getContainerClient(containerName);

const blobClient = containerClient.getBlockBlobClient(blobName);
try {
await blobClient.upload("hello", 5, { tier: "Hot" });
assert.fail("Expected MD5 error");
} catch (err) {
assert.deepStrictEqual((err as any).statusCode, 400);
assert.deepStrictEqual((err as any).code, 'InvalidOperation');
assert.deepStrictEqual((err as any).details.errorCode, 'InvalidOperation');
}
});

it("Acquire Lease on Breaking Lease status, if LeaseId not match, throw LeaseIdMismatchWithLease error @loki @sql", async () => {
// TODO: implement the case later
});
Expand Down

0 comments on commit b98dcaa

Please sign in to comment.