Tolerate missing x-ms-blob-type response header#7182
Conversation
|
Thank you for your contribution @clee704! We will review the pull request and get back to you soon. |
There was a problem hiding this comment.
Pull request overview
This PR fixes a crash in azure-storage-blobs when the service returns a successful response (200/206) that omits the x-ms-blob-type header. It makes the header optional in the Swagger customization and updates the generated protocol-layer response parsing to guard header access, preventing a std::out_of_range from std::map::at.
Changes:
- Updated the Swagger AutoRest directives to mark
x-ms-blob-typeasx-nullable(and set a default) forBlob_GetPropertiesandBlob_Downloadresponses. - Updated
rest_client.cppdeserialization to readx-ms-blob-typeonly when present (consistent with existing optional-header handling). - Added a changelog entry describing the bug fix.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| sdk/storage/azure-storage-blobs/swagger/README.md | Adds an AutoRest directive to mark x-ms-blob-type optional for the affected operations so generation guards header access. |
| sdk/storage/azure-storage-blobs/src/rest_client.cpp | Guards x-ms-blob-type header reads in the Download and GetProperties response parsers to avoid std::out_of_range. |
| sdk/storage/azure-storage-blobs/CHANGELOG.md | Documents the crash fix in the Unreleased “Bugs Fixed” section. |
Jinming-Hu
left a comment
There was a problem hiding this comment.
I'm sorry, but we cannot modify generated code like this. I'll look into the issue later today.
|
@clee704 Could you please update your PR with the patch below and leave this change out of the CHANGELOG? We don't officially support using this SDK with OneLake, and I don't want to send the wrong signal or create confusion by implying that we do. diff --git a/sdk/storage/azure-storage-blobs/src/rest_client.cpp b/sdk/storage/azure-storage-blobs/src/rest_client.cpp
index 27a65870b..ab0a62b47 100644
--- a/sdk/storage/azure-storage-blobs/src/rest_client.cpp
+++ b/sdk/storage/azure-storage-blobs/src/rest_client.cpp
@@ -3814,7 +3814,10 @@ namespace Azure { namespace Storage { namespace Blobs {
response.Details.SequenceNumber
= std::stoll(pRawResponse->GetHeaders().at("x-ms-blob-sequence-number"));
}
- response.BlobType = Models::BlobType(pRawResponse->GetHeaders().at("x-ms-blob-type"));
+ if (pRawResponse->GetHeaders().count("x-ms-blob-type") != 0)
+ {
+ response.BlobType = Models::BlobType(pRawResponse->GetHeaders().at("x-ms-blob-type"));
+ }
if (pRawResponse->GetHeaders().count("x-ms-copy-completion-time") != 0)
{
response.Details.CopyCompletedOn = DateTime::Parse(
@@ -4060,7 +4063,10 @@ namespace Azure { namespace Storage { namespace Blobs {
response.ObjectReplicationDestinationPolicyId
= pRawResponse->GetHeaders().at("x-ms-or-policy-id");
}
- response.BlobType = Models::BlobType(pRawResponse->GetHeaders().at("x-ms-blob-type"));
+ if (pRawResponse->GetHeaders().count("x-ms-blob-type") != 0)
+ {
+ response.BlobType = Models::BlobType(pRawResponse->GetHeaders().at("x-ms-blob-type"));
+ }
if (pRawResponse->GetHeaders().count("x-ms-copy-completion-time") != 0)
{
response.CopyCompletedOn = DateTime::Parse(
diff --git a/sdk/storage/azure-storage-blobs/swagger/README.md b/sdk/storage/azure-storage-blobs/swagger/README.md
index 9370cb051..4a55faaf9 100644
--- a/sdk/storage/azure-storage-blobs/swagger/README.md
+++ b/sdk/storage/azure-storage-blobs/swagger/README.md
@@ -1044,6 +1044,8 @@ directive:
$[status_code].headers["x-ms-legal-hold"]["x-nullable"] = true;
$[status_code].headers["x-ms-immutability-policy-until-date"]["x-ms-client-path"] = "Details.ImmutabilityPolicy.ExpiresOn";
$[status_code].headers["x-ms-immutability-policy-mode"]["x-ms-client-path"] = "Details.ImmutabilityPolicy.PolicyMode";
+ $[status_code].headers["x-ms-blob-type"]["x-nullable"] = true;
+ $[status_code].headers["x-ms-blob-type"]["x-ms-client-default"] = "";
delete $[status_code].headers["Accept-Ranges"];
delete $[status_code].headers["Content-Length"];
delete $[status_code].headers["Content-Range"];
@@ -1150,6 +1152,8 @@ directive:
$["x-ms-legal-hold"]["x-ms-client-name"] = "HasLegalHold";
$["x-ms-legal-hold"]["x-ms-client-default"] = false;
$["x-ms-legal-hold"]["x-nullable"] = true;
+ $["x-ms-blob-type"]["x-nullable"] = true;
+ $["x-ms-blob-type"]["x-ms-client-default"] = "";
delete $["Accept-Ranges"];
delete $["x-ms-or"]; |
Get Blob (Blob_Download) and Get Blob Properties (Blob_GetProperties)
deserialize the x-ms-blob-type response header unconditionally
(GetHeaders().at("x-ms-blob-type")), which throws std::out_of_range
when a successful (2xx) response omits it. Mark the header x-nullable on
those two response directives so the generator guards the access, and
apply the matching guard in the generated rest_client.cpp.
a32bd85 to
ae11ed9
Compare
|
Thanks @Jinming-Hu — done. I've updated the PR with your patch (folded the |
Fixes #7180.
Description
BlobClient::GetProperties()andBlobClient::Download()throwstd::out_of_rangewhen a successful (2xx) response omits thex-ms-blob-typeheader, because it is generated as a required header (an unguardedGetHeaders().at("x-ms-blob-type")). The SDK then crashes — with a non-StorageException— on an otherwise-valid 200, e.g. from the OneLake / ADLS Gen2*.dfs.endpoint serving the Blob API, which returns 200 without that header.This marks
x-ms-blob-typeoptional on theBlob_GetPropertiesandBlob_Downloadresponses so the generator guards it with the existingcount(...) != 0idiom — the same treatmentETag/Last-Modifiedalready receive on those responses — and applies the matching guard in the generatedrest_client.cpp. When the header is absent,BlobTypedefaults to an emptyModels::BlobTypeand the read otherwise succeeds.Mirrors the "striped blob" compatibility fix for
x-ms-blob-sequence-number(PR #3932, commit 0e00a3a): same class of problem, same mechanism (anx-nullabledirective plus the matching generated-file guard, committed together).rest_client.cppis AutoRest-generated and the C++ emitter is not publicly runnable, so the generated-file edit here is by hand to match the directive (as #3932 did). Please regenerate to confirm the directive reproduces this output.Pull Request Checklist
storage_retry_policy_test.cpp) assertingGetProperties/Downloadtolerate a 200 withoutx-ms-blob-typecan be added if preferred.