Skip to content

Commit fab80c9

Browse files
authored
feat(core/services-azblob): support user defined metadata (#5274)
1 parent 10d64e2 commit fab80c9

File tree

8 files changed

+52
-41
lines changed

8 files changed

+52
-41
lines changed

core/src/raw/http_util/header.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
// specific language governing permissions and limitations
1616
// under the License.
1717

18+
use std::collections::HashMap;
19+
1820
use base64::engine::general_purpose;
1921
use base64::Engine;
2022
use chrono::DateTime;
@@ -189,6 +191,21 @@ pub fn parse_into_metadata(path: &str, headers: &HeaderMap) -> Result<Metadata>
189191
Ok(m)
190192
}
191193

194+
/// Parse prefixed headers and return a map with the prefix of each header removed.
195+
pub fn parse_prefixed_headers(headers: &HeaderMap, prefix: &str) -> HashMap<String, String> {
196+
headers
197+
.iter()
198+
.filter_map(|(name, value)| {
199+
name.as_str().strip_prefix(prefix).and_then(|stripped_key| {
200+
value
201+
.to_str()
202+
.ok()
203+
.map(|parsed_value| (stripped_key.to_string(), parsed_value.to_string()))
204+
})
205+
})
206+
.collect()
207+
}
208+
192209
/// format content md5 header by given input.
193210
pub fn format_content_md5(bs: &[u8]) -> String {
194211
let mut hasher = md5::Md5::new();

core/src/raw/http_util/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ pub use header::parse_header_to_str;
4949
pub use header::parse_into_metadata;
5050
pub use header::parse_last_modified;
5151
pub use header::parse_location;
52+
pub use header::parse_prefixed_headers;
5253

5354
mod uri;
5455
pub use uri::percent_decode_path;

core/src/services/azblob/backend.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ use reqsign::AzureStorageSigner;
3333
use sha2::Digest;
3434
use sha2::Sha256;
3535

36+
use super::core::constants::X_MS_META_PREFIX;
3637
use super::error::parse_error;
3738
use super::lister::AzblobLister;
3839
use super::writer::AzblobWriter;
@@ -517,6 +518,7 @@ impl Access for AzblobBackend {
517518
write_can_multi: true,
518519
write_with_cache_control: true,
519520
write_with_content_type: true,
521+
write_with_user_metadata: true,
520522

521523
delete: true,
522524
copy: true,
@@ -545,7 +547,17 @@ impl Access for AzblobBackend {
545547
let status = resp.status();
546548

547549
match status {
548-
StatusCode::OK => parse_into_metadata(path, resp.headers()).map(RpStat::new),
550+
StatusCode::OK => {
551+
let headers = resp.headers();
552+
let mut meta = parse_into_metadata(path, headers)?;
553+
554+
let user_meta = parse_prefixed_headers(headers, X_MS_META_PREFIX);
555+
if !user_meta.is_empty() {
556+
meta.with_user_metadata(user_meta);
557+
}
558+
559+
Ok(RpStat::new(meta))
560+
}
549561
_ => Err(parse_error(resp)),
550562
}
551563
}

core/src/services/azblob/core.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use std::time::Duration;
2424
use base64::prelude::BASE64_STANDARD;
2525
use base64::Engine;
2626
use bytes::Bytes;
27+
use constants::X_MS_META_PREFIX;
2728
use http::header::HeaderName;
2829
use http::header::CONTENT_LENGTH;
2930
use http::header::CONTENT_TYPE;
@@ -42,13 +43,14 @@ use uuid::Uuid;
4243
use crate::raw::*;
4344
use crate::*;
4445

45-
mod constants {
46+
pub mod constants {
4647
pub const X_MS_VERSION: &str = "x-ms-version";
4748

4849
pub const X_MS_BLOB_TYPE: &str = "x-ms-blob-type";
4950
pub const X_MS_COPY_SOURCE: &str = "x-ms-copy-source";
5051
pub const X_MS_BLOB_CACHE_CONTROL: &str = "x-ms-blob-cache-control";
5152
pub const X_MS_BLOB_CONDITION_APPENDPOS: &str = "x-ms-blob-condition-appendpos";
53+
pub const X_MS_META_PREFIX: &str = "x-ms-meta-";
5254

5355
// Server-side encryption with customer-provided headers
5456
pub const X_MS_ENCRYPTION_KEY: &str = "x-ms-encryption-key";
@@ -243,12 +245,19 @@ impl AzblobCore {
243245

244246
let mut req = Request::put(&url);
245247

248+
if let Some(user_metadata) = args.user_metadata() {
249+
for (key, value) in user_metadata {
250+
req = req.header(format!("{X_MS_META_PREFIX}{key}"), value)
251+
}
252+
}
253+
246254
// Set SSE headers.
247255
req = self.insert_sse_headers(req);
248256

249257
if let Some(cache_control) = args.cache_control() {
250258
req = req.header(constants::X_MS_BLOB_CACHE_CONTROL, cache_control);
251259
}
260+
252261
if let Some(size) = size {
253262
req = req.header(CONTENT_LENGTH, size)
254263
}

core/src/services/oss/backend.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -489,9 +489,7 @@ impl Access for OssBackend {
489489
match status {
490490
StatusCode::OK => {
491491
let headers = resp.headers();
492-
let mut meta =
493-
self.core
494-
.parse_metadata(path, constants::X_OSS_META_PREFIX, resp.headers())?;
492+
let mut meta = self.core.parse_metadata(path, resp.headers())?;
495493

496494
if let Some(v) = parse_header_to_str(headers, "x-oss-version-id")? {
497495
meta.set_version(v);

core/src/services/oss/core.rs

Lines changed: 6 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@
1515
// specific language governing permissions and limitations
1616
// under the License.
1717

18-
use std::collections::HashMap;
1918
use std::fmt::Debug;
2019
use std::fmt::Formatter;
2120
use std::fmt::Write;
2221
use std::time::Duration;
2322

2423
use bytes::Bytes;
24+
use constants::X_OSS_META_PREFIX;
2525
use http::header::CACHE_CONTROL;
2626
use http::header::CONTENT_DISPOSITION;
2727
use http::header::CONTENT_LENGTH;
@@ -190,7 +190,7 @@ impl OssCore {
190190
"the format of the user metadata key is invalid, please refer the document",
191191
));
192192
}
193-
req = req.header(format!("{}{}", constants::X_OSS_META_PREFIX, key), value)
193+
req = req.header(format!("{X_OSS_META_PREFIX}{key}"), value)
194194
}
195195
}
196196

@@ -213,28 +213,11 @@ impl OssCore {
213213
/// # Notes
214214
///
215215
/// before return the user defined metadata, we'll strip the user_metadata_prefix from the key
216-
pub fn parse_metadata(
217-
&self,
218-
path: &str,
219-
user_metadata_prefix: &str,
220-
headers: &HeaderMap,
221-
) -> Result<Metadata> {
216+
pub fn parse_metadata(&self, path: &str, headers: &HeaderMap) -> Result<Metadata> {
222217
let mut m = parse_into_metadata(path, headers)?;
223-
224-
let data: HashMap<String, String> = headers
225-
.iter()
226-
.filter_map(|(key, _)| {
227-
key.as_str()
228-
.strip_prefix(user_metadata_prefix)
229-
.and_then(|stripped_key| {
230-
parse_header_to_str(headers, key)
231-
.unwrap_or(None)
232-
.map(|val| (stripped_key.to_string(), val.to_string()))
233-
})
234-
})
235-
.collect();
236-
if !data.is_empty() {
237-
m.with_user_metadata(data);
218+
let user_meta = parse_prefixed_headers(headers, X_OSS_META_PREFIX);
219+
if !user_meta.is_empty() {
220+
m.with_user_metadata(user_meta);
238221
}
239222

240223
Ok(m)

core/src/services/s3/backend.rs

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ use std::sync::Arc;
2626
use base64::prelude::BASE64_STANDARD;
2727
use base64::Engine;
2828
use bytes::Buf;
29+
use constants::X_AMZ_META_PREFIX;
2930
use http::Response;
3031
use http::StatusCode;
3132
use log::debug;
@@ -970,18 +971,7 @@ impl Access for S3Backend {
970971
let headers = resp.headers();
971972
let mut meta = parse_into_metadata(path, headers)?;
972973

973-
let user_meta: HashMap<String, String> = headers
974-
.iter()
975-
.filter_map(|(name, _)| {
976-
name.as_str()
977-
.strip_prefix(constants::X_AMZ_META_PREFIX)
978-
.and_then(|stripped_key| {
979-
parse_header_to_str(headers, name)
980-
.unwrap_or(None)
981-
.map(|val| (stripped_key.to_string(), val.to_string()))
982-
})
983-
})
984-
.collect();
974+
let user_meta = parse_prefixed_headers(headers, X_AMZ_META_PREFIX);
985975
if !user_meta.is_empty() {
986976
meta.with_user_metadata(user_meta);
987977
}

core/src/services/s3/core.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ use std::time::Duration;
2727
use base64::prelude::BASE64_STANDARD;
2828
use base64::Engine;
2929
use bytes::Bytes;
30+
use constants::X_AMZ_META_PREFIX;
3031
use http::header::HeaderName;
3132
use http::header::CACHE_CONTROL;
3233
use http::header::CONTENT_DISPOSITION;
@@ -462,7 +463,7 @@ impl S3Core {
462463
// Set user metadata headers.
463464
if let Some(user_metadata) = args.user_metadata() {
464465
for (key, value) in user_metadata {
465-
req = req.header(format!("{}{}", constants::X_AMZ_META_PREFIX, key), value)
466+
req = req.header(format!("{X_AMZ_META_PREFIX}{key}"), value)
466467
}
467468
}
468469

0 commit comments

Comments
 (0)