Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ General:

Blob:

- Bumped the default Blob service API version to `2026-10-06` for `@azure/storage-blob` 12.34.0, including the `endBefore` List Blobs parameter. Requests for the Apache Arrow list format receive XML responses, which the SDK supports as a fallback. For API version `2026-10-06` and later, Put Blob, Put Blob From URL, Put Block, Put Block From URL, Put Page, and Append Block return the computed `x-ms-content-crc64` when the corresponding MD5 checksum is supplied. Blob downloads now expose access-tier headers.
- Fixed block blob uploads with `If-None-Match: *` returning `BlobAlreadyExists` before validating an active lease, matching Azure Storage's `LeaseIdMissing` and lease mismatch error precedence. (issue #2637)
- Fixed service- and container-level Filter Blobs requests failing when the optional `where` query parameter is omitted.
- Fixed blob operations hanging when a client disconnects before the operation queue processes the request. (issue #2575)
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ Compared to V2, Azurite V3 implements a new architecture leveraging code generat

## Features & Key Changes in Azurite V3

- Blob storage features align with Azure Storage API version 2026-06-06 (Refer to support matrix section below)
- Blob storage features align with Azure Storage API version 2026-10-06 (Refer to support matrix section below)
Comment thread
Copilot marked this conversation as resolved.
- SharedKey/Account SAS/Service SAS/Public Access Authentications/OAuth
- Get/Set Blob Service Properties
- Create/List/Delete Containers
Expand Down Expand Up @@ -1066,7 +1066,7 @@ Legacy Azurite V2 supports Azure Storage Blob, Queue and Table services.
Azurite V3 currently only supports Azure Storage blob service. Queue service is supported after V3.2.0-preview.
Table service support is currently under discussion.

Azurite V3 supports features through Azure Storage API version 2026-06-06 and aims to maintain parity with the latest API versions more frequently than legacy Azurite V2.
Azurite V3 supports features through Azure Storage API version 2026-10-06 and aims to maintain parity with the latest API versions more frequently than legacy Azurite V2.

## TypeScript Server Code Generator

Expand Down Expand Up @@ -1100,7 +1100,7 @@ Useful commands:

## Support Matrix

Latest release targets **2026-06-06** API version **blob** service.
Latest release targets **2026-10-06** API version **blob** service.

Detailed support matrix:

Expand Down
56 changes: 52 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 30 additions & 0 deletions src/blob/generated/artifacts/mappers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4112,6 +4112,30 @@ export const BlobDownloadHeaders: msRest.CompositeMapper = {
name: "String"
}
},
accessTier: {
serializedName: "x-ms-access-tier",
type: {
name: "String"
}
},
accessTierInferred: {
serializedName: "x-ms-access-tier-inferred",
type: {
name: "Boolean"
}
},
accessTierChangeTime: {
serializedName: "x-ms-access-tier-change-time",
type: {
name: "DateTimeRfc1123"
}
},
smartAccessTier: {
serializedName: "x-ms-smart-access-tier",
type: {
name: "String"
}
},
blobContentMD5: {
serializedName: "x-ms-blob-content-md5",
type: {
Expand Down Expand Up @@ -4757,6 +4781,12 @@ export const BlockBlobUploadHeaders: msRest.CompositeMapper = {
name: "ByteArray"
}
},
xMsContentCrc64: {
serializedName: "x-ms-content-crc64",
type: {
name: "ByteArray"
}
},
clientRequestId: {
serializedName: "x-ms-client-request-id",
type: {
Expand Down
22 changes: 22 additions & 0 deletions src/blob/generated/artifacts/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4549,6 +4549,22 @@ export interface BlobDownloadHeaders {
* scope.
*/
encryptionScope?: string;
/**
* The tier of the blob.
*/
accessTier?: string;
/**
* Indicates whether the access tier is inferred.
*/
accessTierInferred?: boolean;
/**
* The time the tier was changed on the object.
*/
accessTierChangeTime?: Date;
/**
* The smart access tier of the blob.
*/
smartAccessTier?: string;
/**
* If the blob has a MD5 hash, and if request contains range header (Range or x-ms-range), this
* response header is returned with the value of the whole blob's MD5 value. This value may or
Expand Down Expand Up @@ -5029,6 +5045,12 @@ export interface BlockBlobUploadHeaders {
* is returned so that the client can check for message content integrity.
*/
contentMD5?: Uint8Array;
/**
* This header is returned so that the client can check for message content integrity. The value
* of this header is computed by the Blob service; it is not necessarily the same value specified
* in the request headers.
*/
xMsContentCrc64?: Uint8Array;
/**
* If a client request id header is sent in the request, this header will be present in the
* response with the same value.
Expand Down
14 changes: 9 additions & 5 deletions src/blob/handlers/AppendBlobHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ import {
MAX_APPEND_BLOB_BLOCK_COUNT,
MAX_APPEND_BLOB_BLOCK_SIZE
} from "../utils/constants";
import { computeAndValidateTransactionalChecksums, getTagsFromString } from "../utils/utils";
import {
computeAndValidateTransactionalChecksums,
getTagsFromString,
supportsCrc64ResponseWithMd5
} from "../utils/utils";
import BaseHandler from "./BaseHandler";

export default class AppendBlobHandler extends BaseHandler
Expand Down Expand Up @@ -163,15 +167,15 @@ export default class AppendBlobHandler extends BaseHandler
: contentMD5;
}

// Per the Append Block REST contract, the service always computes a CRC64
// of the appended block and returns it in x-ms-content-crc64.
const includeCRC64 =
contentMD5 === undefined || supportsCrc64ResponseWithMd5(context);
const stream = await this.extentStore.readExtent(extent, blobCtx.contextId);
const { crc64: calculatedCRC64 } =
await computeAndValidateTransactionalChecksums(
stream,
{ md5: contentMD5, crc64: contentCRC64 },
context.contextId,
{ crc64: true }
{ crc64: includeCRC64 }
);

const originOffset = blob.properties.contentLength;
Expand All @@ -198,7 +202,7 @@ export default class AppendBlobHandler extends BaseHandler
eTag: properties.etag,
lastModified: properties.lastModified,
contentMD5: contentMD5Buffer ? new Uint8Array(contentMD5Buffer) : undefined,
xMsContentCrc64: calculatedCRC64,
xMsContentCrc64: includeCRC64 ? calculatedCRC64 : undefined,
clientRequestId: options.requestId,
version: BLOB_API_VERSION,
date,
Expand Down
54 changes: 37 additions & 17 deletions src/blob/handlers/BlockBlobHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import BaseHandler from "./BaseHandler";
import {
computeAndValidateTransactionalChecksums,
getTagsFromString,
supportsCrc64ResponseWithMd5,
validateTransactionalChecksumHeaders
} from "../utils/utils";

Expand Down Expand Up @@ -128,18 +129,25 @@ export default class BlockBlobHandler
);
}

const includeCRC64 =
context.request!.getHeader(HeaderConstants.CONTENT_MD5) !== undefined &&
supportsCrc64ResponseWithMd5(context);

// MD5 is always needed (persisted as the blob's contentMD5 property);
// CRC64 is computed in the same pass only when the client supplied one.
// CRC64 is also returned for 2026-10-06 requests that supply an MD5.
const stream = await this.extentStore.readExtent(
persistency,
context.contextId
);
const { md5: calculatedContentMD5 } =
const {
md5: calculatedContentMD5,
crc64: calculatedContentCRC64
} =
await computeAndValidateTransactionalChecksums(
stream,
{ md5: contentMD5, crc64: contentCRC64 },
context.contextId,
{ md5: true }
{ md5: true, crc64: includeCRC64 }
);

const blob: BlobModel = {
Expand Down Expand Up @@ -198,6 +206,7 @@ export default class BlockBlobHandler
eTag: etag,
lastModified: date,
contentMD5: blob.properties.contentMD5,
xMsContentCrc64: calculatedContentCRC64,
requestId: blobCtx.contextId,
version: BLOB_API_VERSION,
date,
Expand Down Expand Up @@ -311,13 +320,15 @@ export default class BlockBlobHandler
);
let calculatedContentMD5: Uint8Array | undefined;
let calculatedContentCRC64: Uint8Array | undefined;
const includeCRC64 =
expectedContentMD5 === undefined || supportsCrc64ResponseWithMd5(context);
try {
({ md5: calculatedContentMD5, crc64: calculatedContentCRC64 } =
await computeAndValidateTransactionalChecksums(
stream,
{ md5: expectedContentMD5, crc64: expectedContentCRC64 },
context.contextId,
{ md5: true, crc64: true }
{ md5: true, crc64: includeCRC64 }
));
} finally {
(stream as Readable).destroy?.();
Expand Down Expand Up @@ -422,7 +433,7 @@ export default class BlockBlobHandler
eTag: etag,
lastModified: date,
contentMD5: blob.properties.contentMD5,
xMsContentCrc64: calculatedContentCRC64,
xMsContentCrc64: includeCRC64 ? calculatedContentCRC64 : undefined,
requestId: blobCtx.contextId,
version: BLOB_API_VERSION,
date,
Expand Down Expand Up @@ -477,20 +488,25 @@ export default class BlockBlobHandler
}

// Per the Put Block REST contract, the service computes a CRC64 of the
// staged block and echoes it back in x-ms-content-crc64 unless the client
// supplied a Content-MD5 (Azure rejects supplying both). Compute CRC64
// whenever no MD5 was supplied, regardless of whether the client supplied
// a CRC64 themselves.
// staged block and echoes it back unless the client supplied a Content-MD5.
// API version 2026-10-06 adds the CRC64 response when an MD5 is supplied.
const includeContentMD5 =
contentMD5 !== undefined && supportsCrc64ResponseWithMd5(context);
const includeCRC64 =
contentMD5 === undefined || supportsCrc64ResponseWithMd5(context);
const stream = await this.extentStore.readExtent(
persistency,
context.contextId
);
const { crc64: calculatedCRC64 } =
const {
md5: calculatedMD5,
crc64: calculatedCRC64
} =
await computeAndValidateTransactionalChecksums(
stream,
{ md5: contentMD5, crc64: contentCRC64 },
context.contextId,
{ crc64: contentMD5 === undefined }
{ md5: includeContentMD5, crc64: includeCRC64 }
);

const block: BlockModel = {
Expand All @@ -512,7 +528,7 @@ export default class BlockBlobHandler

const response: Models.BlockBlobStageBlockResponse = {
statusCode: 201,
contentMD5: undefined, // TODO: Block content MD5
contentMD5: includeContentMD5 ? calculatedMD5 : undefined,
xMsContentCrc64: calculatedCRC64,
requestId: blobCtx.contextId,
version: BLOB_API_VERSION,
Expand Down Expand Up @@ -621,10 +637,9 @@ export default class BlockBlobHandler

// Compare the supplied source checksums against the fetched bytes with
// the same helper Put Block uses. The response always echoes an MD5, so
// that one is always computed. A CRC64 is additionally computed - and so
// additionally echoed - only when no source MD5 was supplied, mirroring
// stageBlock; the two source checksum headers are mutually exclusive, so
// a supplied source MD5 means the caller cannot have asked for CRC64.
// that one is always computed. A CRC64 is additionally computed and echoed
// when no source MD5 was supplied, or for API version 2026-10-06 and later.
// The two source checksum headers are mutually exclusive.
// The header shapes were already rejected above, so the only failures
// left are mismatches, which happen after the stream has been read.
// Destroy it regardless so a throw cannot leave the extent handle open.
Expand All @@ -643,7 +658,12 @@ export default class BlockBlobHandler
crc64: options.sourceContentcrc64
},
context.contextId,
{ md5: true, crc64: options.sourceContentMD5 === undefined }
{
md5: true,
crc64:
options.sourceContentMD5 === undefined ||
supportsCrc64ResponseWithMd5(context)
Comment thread
jainakanksha-msft marked this conversation as resolved.
}
));
} finally {
(stream as Readable).destroy?.();
Expand Down
Loading
Loading