diff --git a/doc/xml/release/2025/2.55.0.xml b/doc/xml/release/2025/2.55.0.xml index 27b252941..df8748dee 100644 --- a/doc/xml/release/2025/2.55.0.xml +++ b/doc/xml/release/2025/2.55.0.xml @@ -131,6 +131,17 @@

Use lz4 for protocol compression.

+ + + + + + + + +

Calculate content-md5 on S3 only when required.

+
+ diff --git a/src/storage/s3/storage.c b/src/storage/s3/storage.c index 9c41c2ec1..a4e3c12e0 100644 --- a/src/storage/s3/storage.c +++ b/src/storage/s3/storage.c @@ -452,6 +452,7 @@ storageS3RequestAsync(StorageS3 *const this, const String *const verb, const Str FUNCTION_LOG_PARAM(HTTP_HEADER, param.header); FUNCTION_LOG_PARAM(HTTP_QUERY, param.query); FUNCTION_LOG_PARAM(BUFFER, param.content); + FUNCTION_LOG_PARAM(BOOL, param.contentMd5); FUNCTION_LOG_PARAM(BOOL, param.sseKms); FUNCTION_LOG_PARAM(BOOL, param.sseC); FUNCTION_LOG_PARAM(BOOL, param.tag); @@ -473,9 +474,11 @@ storageS3RequestAsync(StorageS3 *const this, const String *const verb, const Str requestHeader, HTTP_HEADER_CONTENT_LENGTH_STR, param.content == NULL || bufEmpty(param.content) ? ZERO_STR : strNewFmt("%zu", bufUsed(param.content))); - // Calculate content-md5 header if there is content - if (param.content != NULL) + // Calculate content-md5 header when required + if (param.contentMd5) { + ASSERT(param.content != NULL && !bufEmpty(param.content)); + httpHeaderAdd( requestHeader, HTTP_HEADER_CONTENT_MD5_STR, strNewEncode(encodingBase64, cryptoHashOne(hashTypeMd5, param.content))); @@ -607,6 +610,7 @@ storageS3Request(StorageS3 *const this, const String *const verb, const String * FUNCTION_LOG_PARAM(HTTP_HEADER, param.header); FUNCTION_LOG_PARAM(HTTP_QUERY, param.query); FUNCTION_LOG_PARAM(BUFFER, param.content); + FUNCTION_LOG_PARAM(BOOL, param.contentMd5); FUNCTION_LOG_PARAM(BOOL, param.allowMissing); FUNCTION_LOG_PARAM(BOOL, param.contentIo); FUNCTION_LOG_PARAM(BOOL, param.sseKms); @@ -615,8 +619,8 @@ storageS3Request(StorageS3 *const this, const String *const verb, const String * FUNCTION_LOG_END(); HttpRequest *const request = storageS3RequestAsyncP( - this, verb, path, .header = param.header, .query = param.query, .content = param.content, .sseKms = param.sseKms, - .sseC = param.sseC, .tag = param.tag); + this, verb, path, .header = param.header, .query = param.query, .content = param.content, .contentMd5 = param.contentMd5, + .sseKms = param.sseKms, .sseC = param.sseC, .tag = param.tag); HttpResponse *const result = storageS3ResponseP( request, .allowMissing = param.allowMissing, .contentIo = param.contentIo); @@ -1041,7 +1045,8 @@ storageS3PathRemoveInternal(StorageS3 *const this, HttpRequest *const request, X HttpQuery *const query = httpQueryAdd(httpQueryNewP(), S3_QUERY_DELETE_STR, EMPTY_STR); Buffer *const content = xmlDocumentBuf(xml); - result = storageS3RequestAsyncP(this, HTTP_VERB_POST_STR, FSLASH_STR, .query = query, .content = content); + result = storageS3RequestAsyncP( + this, HTTP_VERB_POST_STR, FSLASH_STR, .query = query, .content = content, .contentMd5 = true); httpQueryFree(query); bufFree(content); diff --git a/src/storage/s3/storage.intern.h b/src/storage/s3/storage.intern.h index 24be5bce9..710e45b70 100644 --- a/src/storage/s3/storage.intern.h +++ b/src/storage/s3/storage.intern.h @@ -22,6 +22,7 @@ typedef struct StorageS3RequestAsyncParam const HttpHeader *header; // Headers const HttpQuery *query; // Query parameters const Buffer *content; // Request content + bool contentMd5; // MD5 content checksum required? bool sseKms; // Enable server-side encryption? bool sseC; // Enable server-side encryption with customer-provided keys? bool tag; // Add tags when available? @@ -52,6 +53,7 @@ typedef struct StorageS3RequestParam const HttpHeader *header; // Headers const HttpQuery *query; // Query parameters const Buffer *content; // Request content + bool contentMd5; // MD5 content checksum required? bool allowMissing; // Allow missing files (caller can check response code) bool contentIo; // Is IoRead interface required to read content? bool sseKms; // Enable server-side encryption? diff --git a/test/src/module/storage/s3Test.c b/test/src/module/storage/s3Test.c index 5c26566df..49b022524 100644 --- a/test/src/module/storage/s3Test.c +++ b/test/src/module/storage/s3Test.c @@ -34,6 +34,7 @@ typedef struct TestRequestParam const char *token; const char *tag; bool requesterPays; + bool contentMd5; } TestRequestParam; #define testRequestP(write, s3, verb, path, ...) \ @@ -68,7 +69,7 @@ testRequest(IoWrite *write, Storage *s3, const char *verb, const char *path, Tes "authorization:AWS4-HMAC-SHA256 Credential=%s/\?\?\?\?\?\?\?\?/us-east-1/s3/aws4_request,SignedHeaders=", param.accessKey == NULL ? strZ(driver->accessKey) : param.accessKey); - if (param.content != NULL) + if (param.contentMd5) strCatZ(request, "content-md5;"); strCatZ(request, "host;"); @@ -105,7 +106,7 @@ testRequest(IoWrite *write, Storage *s3, const char *verb, const char *path, Tes strCatFmt(request, "content-length:%zu\r\n", param.content != NULL ? strlen(param.content) : 0); // Add md5 - if (param.content != NULL) + if (param.contentMd5) { strCatFmt( request, "content-md5:%s\r\n", strZ(strNewEncode(encodingBase64, cryptoHashOne(hashTypeMd5, BUFSTRZ(param.content))))); @@ -869,7 +870,6 @@ testRun(void) "*** Request Headers ***:\n" "authorization: \n" "content-length: 205\n" - "content-md5: 37smUM6Ah2/EjZbp420dPw==\n" "host: bucket.s3.amazonaws.com\n" "x-amz-content-sha256: 0838a79dfbddc2128d28fb4fa8d605e0a8e6d1355094000f39b6eb3feff4641f\n" "x-amz-date: \n" @@ -1317,7 +1317,7 @@ testRun(void) ""); testRequestP( - service, s3, HTTP_VERB_POST, "/bucket/?delete=", + service, s3, HTTP_VERB_POST, "/bucket/?delete=", .contentMd5 = true, .content = "\n" "true" @@ -1378,7 +1378,7 @@ testRun(void) ""); testRequestP( - service, s3, HTTP_VERB_POST, "/bucket/?delete=", + service, s3, HTTP_VERB_POST, "/bucket/?delete=", .contentMd5 = true, .content = "\n" "true" @@ -1388,7 +1388,7 @@ testRun(void) testResponseP(service); testRequestP( - service, s3, HTTP_VERB_POST, "/bucket/?delete=", + service, s3, HTTP_VERB_POST, "/bucket/?delete=", .contentMd5 = true, .content = "\n" "true" @@ -1417,7 +1417,7 @@ testRun(void) ""); testRequestP( - service, s3, HTTP_VERB_POST, "/bucket/?delete=", + service, s3, HTTP_VERB_POST, "/bucket/?delete=", .contentMd5 = true, .content = "\n" "true"