You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@opendal.apache.org by xu...@apache.org on 2026/08/26 06:59:37 UTC
(opendal) branch main updated: fix(services/azblob): carry content type and user metadata on Put Block List (#8148)
This is an automated email from the ASF dual-hosted git repository.
Xuanwo pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/opendal.git
The following commit(s) were added to refs/heads/main by this push:
new e7988f72e fix(services/azblob): carry content type and user metadata on Put Block List (#8148)
e7988f72e is described below
commit e7988f72ed4c1e00d1ca6b9188435a6ffbdc96c0
Author: ZIHAN DAI <99...@users.noreply.github.com>
AuthorDate: Wed Aug 26 16:59:31 2026 +1000
fix(services/azblob): carry content type and user metadata on Put Block List (#8148)
azblob declares write_with_content_type and write_with_user_metadata, but
only azblob_put_blob_request honours them, and BlockWriter reaches that
only for a one-shot write. A chunked write commits through Put Block List,
which set cache-control and the conditional headers but neither the content
type nor x-ms-meta-*, so both were dropped.
Put Block List is where Azure applies blob properties; the same headers on
the individual Put Block requests are ignored, which is why setting them
there looked like coverage without being any.
Adds a test asserting both headers on the built request.
Co-authored-by: Zihan Dai <dz...@gmail.com>
---
core/services/azblob/src/core.rs | 63 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 63 insertions(+)
diff --git a/core/services/azblob/src/core.rs b/core/services/azblob/src/core.rs
index c70ad3a36..09ce0f9ca 100644
--- a/core/services/azblob/src/core.rs
+++ b/core/services/azblob/src/core.rs
@@ -55,6 +55,7 @@ pub mod constants {
pub const X_MS_COPY_SOURCE: &str = "x-ms-copy-source";
pub const X_MS_COPY_SOURCE_RANGE: &str = "x-ms-source-range";
pub const X_MS_BLOB_CACHE_CONTROL: &str = "x-ms-blob-cache-control";
+ pub const X_MS_BLOB_CONTENT_TYPE: &str = "x-ms-blob-content-type";
pub const X_MS_BLOB_CONDITION_APPENDPOS: &str = "x-ms-blob-condition-appendpos";
pub const X_MS_META_PREFIX: &str = "x-ms-meta-";
@@ -590,6 +591,17 @@ impl AzblobCore {
req = req.header(constants::X_MS_BLOB_CACHE_CONTROL, cache_control);
}
+ // Put Block List is where Azure applies the blob's properties and metadata;
+ // the headers on the individual Put Block requests are ignored.
+ if let Some(ty) = args.content_type() {
+ req = req.header(constants::X_MS_BLOB_CONTENT_TYPE, ty);
+ }
+ if let Some(user_metadata) = args.user_metadata() {
+ for (key, value) in user_metadata {
+ req = req.header(format!("{X_MS_META_PREFIX}{key}"), value);
+ }
+ }
+
// Put Block List is the request that actually commits a blocked write, so the
// write's preconditions have to be evaluated here rather than on Put Block.
if args.if_not_exists() {
@@ -900,6 +912,10 @@ mod tests {
use quick_xml::de;
use super::*;
+ use std::collections::HashMap;
+
+ use reqsign_azure_storage::RequestSigner;
+ use reqsign_azure_storage::StaticCredentialProvider;
#[test]
fn test_parse_xml() {
@@ -1081,6 +1097,53 @@ mod tests {
}
/// This example is from https://learn.microsoft.com/en-us/rest/api/storageservices/put-block-list?tabs=microsoft-entra-id
+ /// Put Block List is the commit point of a blocked write, and the only request
+ /// in that sequence where Azure applies blob properties and metadata. azblob
+ /// declares `write_with_content_type` and `write_with_user_metadata`, so both
+ /// have to be carried here as well as on the one-shot Put Blob path.
+ #[test]
+ fn test_put_block_list_carries_content_type_and_user_metadata() {
+ let core = AzblobCore {
+ info: ServiceInfo::new("azblob", "/", "c"),
+ capability: Capability::default(),
+ container: "c".to_string(),
+ root: "/".to_string(),
+ endpoint: "https://acc.blob.core.windows.net".to_string(),
+ encryption_key: None,
+ encryption_key_sha256: None,
+ encryption_algorithm: None,
+ skip_signature: true,
+ signer: Signer::new(
+ Context::new(),
+ StaticCredentialProvider::new_shared_key("acc", "a2V5"),
+ RequestSigner::new(),
+ ),
+ };
+
+ let mut meta = HashMap::new();
+ meta.insert("k".to_string(), "v".to_string());
+ let args = OpWrite::default()
+ .with_content_type("application/json")
+ .with_user_metadata(meta);
+
+ let req = core
+ .azblob_complete_put_block_list_request("a.txt", vec![Uuid::nil()], &args)
+ .expect("must build");
+
+ assert_eq!(
+ req.headers()
+ .get(constants::X_MS_BLOB_CONTENT_TYPE)
+ .map(|v| v.to_str().unwrap()),
+ Some("application/json")
+ );
+ assert_eq!(
+ req.headers()
+ .get("x-ms-meta-k")
+ .map(|v| v.to_str().unwrap()),
+ Some("v")
+ );
+ }
+
#[test]
fn test_serialize_put_block_list_request() {
let req = PutBlockListRequest {