Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package aws-c-s3 for openSUSE:Factory 
checked in at 2026-06-19 16:38:01
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/aws-c-s3 (Old)
 and      /work/SRC/openSUSE:Factory/.aws-c-s3.new.1956 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "aws-c-s3"

Fri Jun 19 16:38:01 2026 rev:42 rq:1360559 version:0.12.6

Changes:
--------
--- /work/SRC/openSUSE:Factory/aws-c-s3/aws-c-s3.changes        2026-06-15 
19:53:11.787955128 +0200
+++ /work/SRC/openSUSE:Factory/.aws-c-s3.new.1956/aws-c-s3.changes      
2026-06-19 17:13:41.226172467 +0200
@@ -1,0 +2,8 @@
+Mon Jun 15 09:49:09 UTC 2026 - John Paul Adrian Glaubitz 
<[email protected]>
+
+- Update to version 0.12.6
+  * Support s2n-tls on macOS by @sfod in (#640)
+  * fix copy object mpu by @sbiscigl in (#643)
+  * CopyObject limitations by @TingDaoK in (#641)
+
+-------------------------------------------------------------------

Old:
----
  v0.12.5.tar.gz

New:
----
  v0.12.6.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ aws-c-s3.spec ++++++
--- /var/tmp/diff_new_pack.4luoqD/_old  2026-06-19 17:13:42.946231308 +0200
+++ /var/tmp/diff_new_pack.4luoqD/_new  2026-06-19 17:13:42.950231445 +0200
@@ -19,7 +19,7 @@
 %define library_version 1.0.0
 %define library_soversion 0unstable
 Name:           aws-c-s3
-Version:        0.12.5
+Version:        0.12.6
 Release:        0
 Summary:        AWS Cross-Platform, C99 wrapper for cryptography primitives
 License:        Apache-2.0

++++++ v0.12.5.tar.gz -> v0.12.6.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aws-c-s3-0.12.5/.github/workflows/ci.yml 
new/aws-c-s3-0.12.6/.github/workflows/ci.yml
--- old/aws-c-s3-0.12.5/.github/workflows/ci.yml        2026-06-02 
23:10:16.000000000 +0200
+++ new/aws-c-s3-0.12.6/.github/workflows/ci.yml        2026-06-11 
20:12:01.000000000 +0200
@@ -228,8 +228,46 @@
         source .venv/bin/activate
         python3 -c "from urllib.request import urlretrieve; urlretrieve('${{ 
env.BUILDER_HOST }}/${{ env.BUILDER_SOURCE }}/${{ env.BUILDER_VERSION 
}}/builder.pyz?run=${{ env.RUN }}', 'builder')"
         chmod a+x builder
+        ./builder build -p ${{ env.PACKAGE_NAME }} 
--cmake-extra=-DASSERT_LOCK_HELD=ON 
--cmake-extra=-DCMAKE_NO_SYSTEM_FROM_IMPORTED=ON
+
+  macos-s2n:
+    runs-on: macos-14 # latest
+    env:
+      AWS_CRT_USE_NON_FIPS_TLS_13: 1
+    steps:
+    - uses: aws-actions/configure-aws-credentials@v6
+      with:
+        role-to-assume: ${{ env.CRT_CI_ROLE }}
+        aws-region: ${{ env.AWS_DEFAULT_REGION }}
+    - name: Checkout Sources
+      uses: actions/checkout@v6
+    - name: Build ${{ env.PACKAGE_NAME }} + consumers
+      run: |
+        python3 -m venv .venv
+        source .venv/bin/activate
+        python3 -c "from urllib.request import urlretrieve; urlretrieve('${{ 
env.BUILDER_HOST }}/${{ env.BUILDER_SOURCE }}/${{ env.BUILDER_VERSION 
}}/builder.pyz?run=${{ env.RUN }}', 'builder')"
+        chmod a+x builder
         ./builder build -p ${{ env.PACKAGE_NAME }} 
--cmake-extra=-DASSERT_LOCK_HELD=ON
 
+  macos-x64-s2n:
+    runs-on: macos-14-large # latest
+    env:
+      AWS_CRT_USE_NON_FIPS_TLS_13: 1
+    steps:
+    - uses: aws-actions/configure-aws-credentials@v6
+      with:
+        role-to-assume: ${{ env.CRT_CI_ROLE }}
+        aws-region: ${{ env.AWS_DEFAULT_REGION }}
+    - name: Checkout Sources
+      uses: actions/checkout@v6
+    - name: Build ${{ env.PACKAGE_NAME }} + consumers
+      run: |
+        python3 -m venv .venv
+        source .venv/bin/activate
+        python3 -c "from urllib.request import urlretrieve; urlretrieve('${{ 
env.BUILDER_HOST }}/${{ env.BUILDER_SOURCE }}/${{ env.BUILDER_VERSION 
}}/builder.pyz?run=${{ env.RUN }}', 'builder')"
+        chmod a+x builder
+        ./builder build -p ${{ env.PACKAGE_NAME }} 
--cmake-extra=-DASSERT_LOCK_HELD=ON 
--cmake-extra=-DCMAKE_NO_SYSTEM_FROM_IMPORTED=ON
+
   # Test downstream repos.
   # This should not be required because we can run into a chicken and egg 
problem if there is a change that needs some fix in a downstream repo.
   downstream:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/aws-c-s3-0.12.5/include/aws/s3/private/s3_request_messages.h 
new/aws-c-s3-0.12.6/include/aws/s3/private/s3_request_messages.h
--- old/aws-c-s3-0.12.5/include/aws/s3/private/s3_request_messages.h    
2026-06-02 23:10:16.000000000 +0200
+++ new/aws-c-s3-0.12.6/include/aws/s3/private/s3_request_messages.h    
2026-06-11 20:12:01.000000000 +0200
@@ -122,7 +122,22 @@
 
 /* Create an HTTP request for an S3 Complete-Multipart-Upload request. Creates 
the necessary XML payload using the
  * passed in array list of `struct aws_s3_mpu_part_info *`. Buffer passed in 
will be used to store
- * said XML payload, which will be used as the body. */
+ * said XML payload, which will be used as the body. The x-amz-mp-object-size 
header is taken from object_size; pass
+ * the known total object size, or 0 to omit the header (e.g. a streaming 
upload of unknown length). */
+AWS_S3_API
+struct aws_http_message 
*aws_s3_complete_multipart_message_with_object_size_new(
+    struct aws_allocator *allocator,
+    struct aws_http_message *base_message,
+    struct aws_byte_buf *body_buffer,
+    const struct aws_string *upload_id,
+    const struct aws_array_list *parts,
+    const struct aws_s3_meta_request_checksum_config_storage *checksum_config,
+    uint64_t object_size);
+
+/* Convenience wrapper over 
aws_s3_complete_multipart_message_with_object_size_new() that derives the 
object size from
+ * base_message's Content-Length (0 if absent/unparseable). Suitable for the 
multipart upload path, where the base PUT
+ * carries the full object length; the copy path must call the 
_with_object_size_ variant directly because its base
+ * CopyObject is bodyless. */
 AWS_S3_API
 struct aws_http_message *aws_s3_complete_multipart_message_new(
     struct aws_allocator *allocator,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aws-c-s3-0.12.5/include/aws/s3/s3_client.h 
new/aws-c-s3-0.12.6/include/aws/s3/s3_client.h
--- old/aws-c-s3-0.12.5/include/aws/s3/s3_client.h      2026-06-02 
23:10:16.000000000 +0200
+++ new/aws-c-s3-0.12.6/include/aws/s3/s3_client.h      2026-06-11 
20:12:01.000000000 +0200
@@ -84,6 +84,11 @@
      *   source will not work
      * 3. source bucket is assumed to be in the same region as dest
      * 4. source bucket and dest bucket must both be either directory buckets 
or regular buckets.
+     * 5. on the multipart copy path, metadata, tags, and annotations are not
+     *   automatically copied from the source object. The caller must set
+     *   `x-amz-metadata-directive: REPLACE` and include all desired metadata
+     *   headers and `x-amz-tagging` on the request. Single-part copies (< 1GB)
+     *   are not affected as S3 CopyObject natively handles COPY directives.
      *
      * Provide the `meta_request_options.copy_source_uri` to bypass limitation 
1 & 2.
      */
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aws-c-s3-0.12.5/source/s3_copy_object.c 
new/aws-c-s3-0.12.6/source/s3_copy_object.c
--- old/aws-c-s3-0.12.5/source/s3_copy_object.c 2026-06-02 23:10:16.000000000 
+0200
+++ new/aws-c-s3-0.12.6/source/s3_copy_object.c 2026-06-11 20:12:01.000000000 
+0200
@@ -489,14 +489,17 @@
             aws_byte_buf_reset(&request->request_body, false);
 
             /* Build the message to complete our multipart upload, which 
includes a payload describing all of our
-             * completed parts. */
-            message = aws_s3_complete_multipart_message_new(
+             * completed parts. The object size (discovered via the HEAD on 
the source) is passed explicitly so the
+             * x-amz-mp-object-size header matches what S3 computes from the 
copied parts; the base CopyObject request
+             * is bodyless, so its Content-Length cannot be used as the source 
of that value. */
+            message = aws_s3_complete_multipart_message_with_object_size_new(
                 meta_request->allocator,
                 meta_request->initial_request_message,
                 &request->request_body,
                 copy_object->upload_id,
                 &copy_object->synced_data.part_list,
-                NULL);
+                NULL,
+                copy_object->synced_data.content_length);
 
             break;
         }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aws-c-s3-0.12.5/source/s3_request_messages.c 
new/aws-c-s3-0.12.6/source/s3_request_messages.c
--- old/aws-c-s3-0.12.5/source/s3_request_messages.c    2026-06-02 
23:10:16.000000000 +0200
+++ new/aws-c-s3-0.12.6/source/s3_request_messages.c    2026-06-11 
20:12:01.000000000 +0200
@@ -25,6 +25,13 @@
     AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("if-none-match"),
     AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("x-amz-create-session-mode"),
     AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("x-amz-sdk-checksum-algorithm"),
+    AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("x-amz-metadata-directive"),
+    AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("x-amz-tagging-directive"),
+    AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("x-amz-object-annotation-directive"),
+    AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("x-amz-copy-source-if-match"),
+    AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("x-amz-copy-source-if-none-match"),
+    
AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("x-amz-copy-source-if-modified-since"),
+    
AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("x-amz-copy-source-if-unmodified-since"),
 };
 
 const size_t g_s3_create_multipart_upload_excluded_headers_count =
@@ -56,6 +63,9 @@
     AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("x-amz-object-lock-legal-hold"),
     AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("if-none-match"),
     AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("x-amz-create-session-mode"),
+    AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("x-amz-metadata-directive"),
+    AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("x-amz-tagging-directive"),
+    AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("x-amz-object-annotation-directive"),
 };
 
 const size_t g_s3_upload_part_excluded_headers_count = 
AWS_ARRAY_SIZE(g_s3_upload_part_excluded_headers);
@@ -678,13 +688,14 @@
 static const struct aws_byte_cursor s_close_bracket = 
AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL(">");
 static const struct aws_byte_cursor s_close_bracket_new_line = 
AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL(">\n");
 /* Create a complete-multipart message, which includes an XML payload of all 
completed parts. */
-struct aws_http_message *aws_s3_complete_multipart_message_new(
+struct aws_http_message 
*aws_s3_complete_multipart_message_with_object_size_new(
     struct aws_allocator *allocator,
     struct aws_http_message *base_message,
     struct aws_byte_buf *body_buffer,
     const struct aws_string *upload_id,
     const struct aws_array_list *parts,
-    const struct aws_s3_meta_request_checksum_config_storage *checksum_config) 
{
+    const struct aws_s3_meta_request_checksum_config_storage *checksum_config,
+    uint64_t object_size) {
     AWS_PRECONDITION(allocator);
     AWS_PRECONDITION(base_message);
     AWS_PRECONDITION(body_buffer);
@@ -696,8 +707,6 @@
     struct aws_http_message *message = NULL;
     bool set_checksums =
         checksum_config && (checksum_config->location != AWS_SCL_NONE || 
checksum_config->has_full_object_checksum);
-    const struct aws_http_headers *initial_message_headers = 
aws_http_message_get_headers(base_message);
-    AWS_ASSERT(initial_message_headers);
     if (set_checksums) {
         mpu_algorithm_checksum_name =
             
aws_get_completed_part_name_from_checksum_algorithm(checksum_config->checksum_algorithm);
@@ -750,11 +759,15 @@
             goto error_clean_up;
         }
     }
-    struct aws_byte_cursor content_length_cursor;
-    if (aws_http_headers_get(initial_message_headers, 
g_content_length_header_name, &content_length_cursor) ==
-        AWS_OP_SUCCESS) {
-        /* Set content-length from base message as x-amz-mp-object-size. */
-        if (aws_http_headers_set(headers, 
aws_byte_cursor_from_c_str("x-amz-mp-object-size"), content_length_cursor)) {
+    /* Send the total object size as x-amz-mp-object-size so S3 can validate 
the assembled object. object_size of 0
+     * means "unknown" (e.g. a streaming upload of unknown length); in that 
case the header is omitted, matching the
+     * prior behavior of skipping it when the base message had no 
Content-Length. */
+    if (object_size > 0) {
+        char object_size_buffer[32];
+        int object_size_len = snprintf(object_size_buffer, 
sizeof(object_size_buffer), "%" PRIu64, object_size);
+        struct aws_byte_cursor object_size_cursor =
+            aws_byte_cursor_from_array(object_size_buffer, 
(size_t)object_size_len);
+        if (aws_http_headers_set(headers, 
aws_byte_cursor_from_c_str("x-amz-mp-object-size"), object_size_cursor)) {
             goto error_clean_up;
         }
     }
@@ -855,6 +868,32 @@
     return NULL;
 }
 
+struct aws_http_message *aws_s3_complete_multipart_message_new(
+    struct aws_allocator *allocator,
+    struct aws_http_message *base_message,
+    struct aws_byte_buf *body_buffer,
+    const struct aws_string *upload_id,
+    const struct aws_array_list *parts,
+    const struct aws_s3_meta_request_checksum_config_storage *checksum_config) 
{
+
+    /* Derive the object size from the base message's Content-Length, as the 
multipart upload path expects. A missing
+     * or unparseable Content-Length (e.g. a streaming upload of unknown 
length) yields 0, which tells the delegate to
+     * omit the x-amz-mp-object-size header. */
+    uint64_t object_size = 0;
+    struct aws_byte_cursor content_length_cursor;
+    const struct aws_http_headers *initial_message_headers = 
aws_http_message_get_headers(base_message);
+    if (initial_message_headers != NULL &&
+        aws_http_headers_get(initial_message_headers, 
g_content_length_header_name, &content_length_cursor) ==
+            AWS_OP_SUCCESS) {
+        if (aws_byte_cursor_utf8_parse_u64(content_length_cursor, 
&object_size)) {
+            object_size = 0;
+        }
+    }
+
+    return aws_s3_complete_multipart_message_with_object_size_new(
+        allocator, base_message, body_buffer, upload_id, parts, 
checksum_config, object_size);
+}
+
 struct aws_http_message *aws_s3_abort_multipart_upload_message_new(
     struct aws_allocator *allocator,
     struct aws_http_message *base_message,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aws-c-s3-0.12.5/tests/CMakeLists.txt 
new/aws-c-s3-0.12.6/tests/CMakeLists.txt
--- old/aws-c-s3-0.12.5/tests/CMakeLists.txt    2026-06-02 23:10:16.000000000 
+0200
+++ new/aws-c-s3-0.12.6/tests/CMakeLists.txt    2026-06-11 20:12:01.000000000 
+0200
@@ -344,6 +344,8 @@
 add_net_test_case(test_s3_copy_source_prefixed_by_slash)
 add_net_test_case(test_s3_copy_invalid_source_uri)
 add_net_test_case(test_s3_copy_source_prefixed_by_slash_multipart)
+add_net_test_case(test_s3_copy_object_with_replace_metadata_and_tagging)
+add_net_test_case(test_s3_multipart_copy_object_with_replace_metadata_and_tagging)
 add_net_test_case(test_s3_put_pause_resume_happy_path)
 add_net_test_case(test_s3_put_pause_resume_all_parts_done)
 add_net_test_case(test_s3_put_pause_resume_invalid_resume_data)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aws-c-s3-0.12.5/tests/s3_data_plane_tests.c 
new/aws-c-s3-0.12.6/tests/s3_data_plane_tests.c
--- old/aws-c-s3-0.12.5/tests/s3_data_plane_tests.c     2026-06-02 
23:10:16.000000000 +0200
+++ new/aws-c-s3-0.12.6/tests/s3_data_plane_tests.c     2026-06-11 
20:12:01.000000000 +0200
@@ -7820,6 +7820,253 @@
     return AWS_OP_SUCCESS;
 }
 
+/**
+ * Helper: copy object then verify metadata and tags on the destination.
+ * If use_replace: sets REPLACE directives with explicit metadata/tags on the 
request.
+ * If !use_replace: sets COPY directives (no metadata/tags on request).
+ * expect_metadata: whether HeadObject should have the metadata after copy.
+ */
+static int s_test_s3_copy_object_properties_helper(
+    struct aws_allocator *allocator,
+    struct aws_byte_cursor source_key,
+    struct aws_byte_cursor destination_key,
+    bool use_replace,
+    bool expect_metadata) {
+
+    struct aws_s3_tester tester;
+    AWS_ZERO_STRUCT(tester);
+    ASSERT_SUCCESS(aws_s3_tester_init(allocator, &tester));
+
+    struct aws_byte_cursor source_bucket = g_test_bucket_name;
+
+    char endpoint[1024];
+    snprintf(
+        endpoint,
+        sizeof(endpoint),
+        "%.*s.s3.%s.amazonaws.com",
+        (int)source_bucket.len,
+        source_bucket.ptr,
+        g_test_s3_region.ptr);
+
+    char copy_source_value[1024];
+    snprintf(
+        copy_source_value,
+        sizeof(copy_source_value),
+        "%.*s/%.*s",
+        (int)source_bucket.len,
+        source_bucket.ptr,
+        (int)source_key.len,
+        source_key.ptr);
+
+    char destination_path[512];
+    snprintf(destination_path, sizeof(destination_path), "/%.*s", 
(int)destination_key.len, destination_key.ptr);
+
+    /* Construct CopyObject request */
+    struct aws_http_message *message = aws_http_message_new_request(allocator);
+    ASSERT_NOT_NULL(message);
+    ASSERT_SUCCESS(aws_http_message_set_request_path(message, 
aws_byte_cursor_from_c_str(destination_path)));
+    ASSERT_SUCCESS(aws_http_message_set_request_method(message, 
aws_http_method_put));
+
+    struct aws_http_headers *headers = aws_http_message_get_headers(message);
+    ASSERT_SUCCESS(aws_http_headers_add(headers, g_host_header_name, 
aws_byte_cursor_from_c_str(endpoint)));
+    ASSERT_SUCCESS(aws_http_headers_add(
+        headers, aws_byte_cursor_from_c_str("x-amz-copy-source"), 
aws_byte_cursor_from_c_str(copy_source_value)));
+
+    if (use_replace) {
+        ASSERT_SUCCESS(aws_http_headers_add(
+            headers, aws_byte_cursor_from_c_str("x-amz-metadata-directive"), 
aws_byte_cursor_from_c_str("REPLACE")));
+        ASSERT_SUCCESS(aws_http_headers_add(
+            headers, aws_byte_cursor_from_c_str("x-amz-meta-test-key"), 
aws_byte_cursor_from_c_str("test-value")));
+        ASSERT_SUCCESS(aws_http_headers_add(
+            headers, aws_byte_cursor_from_c_str("Content-Type"), 
aws_byte_cursor_from_c_str("application/x-replaced")));
+        ASSERT_SUCCESS(aws_http_headers_add(
+            headers, aws_byte_cursor_from_c_str("Content-Disposition"), 
aws_byte_cursor_from_c_str("attachment")));
+        ASSERT_SUCCESS(aws_http_headers_add(
+            headers, aws_byte_cursor_from_c_str("Content-Language"), 
aws_byte_cursor_from_c_str("en-US")));
+        ASSERT_SUCCESS(aws_http_headers_add(
+            headers, aws_byte_cursor_from_c_str("Cache-Control"), 
aws_byte_cursor_from_c_str("max-age=3600")));
+        ASSERT_SUCCESS(aws_http_headers_add(
+            headers, aws_byte_cursor_from_c_str("x-amz-tagging-directive"), 
aws_byte_cursor_from_c_str("REPLACE")));
+        ASSERT_SUCCESS(aws_http_headers_add(
+            headers, aws_byte_cursor_from_c_str("x-amz-tagging"), 
aws_byte_cursor_from_c_str("env=test&feature=copy")));
+    } else {
+        ASSERT_SUCCESS(aws_http_headers_add(
+            headers, aws_byte_cursor_from_c_str("x-amz-metadata-directive"), 
aws_byte_cursor_from_c_str("COPY")));
+        ASSERT_SUCCESS(aws_http_headers_add(
+            headers, aws_byte_cursor_from_c_str("x-amz-tagging-directive"), 
aws_byte_cursor_from_c_str("COPY")));
+    }
+
+    /* Execute the copy */
+    struct aws_s3_meta_request_test_results copy_results;
+    aws_s3_meta_request_test_results_init(&copy_results, allocator);
+
+    struct aws_s3_tester_meta_request_options options = {
+        .allocator = allocator,
+        .meta_request_type = AWS_S3_META_REQUEST_TYPE_COPY_OBJECT,
+        .message = message,
+        .validate_type = AWS_S3_TESTER_VALIDATE_TYPE_EXPECT_SUCCESS,
+    };
+
+    ASSERT_SUCCESS(aws_s3_tester_send_meta_request_with_options(&tester, 
&options, &copy_results));
+    ASSERT_INT_EQUALS(AWS_HTTP_STATUS_CODE_200_OK, 
copy_results.finished_response_status);
+
+    aws_s3_meta_request_test_results_clean_up(&copy_results);
+    aws_http_message_release(message);
+    aws_s3_tester_clean_up(&tester);
+
+    /* Verify destination metadata via HeadObject */
+    struct aws_s3_tester head_tester;
+    AWS_ZERO_STRUCT(head_tester);
+    ASSERT_SUCCESS(aws_s3_tester_init(allocator, &head_tester));
+
+    struct aws_http_message *head_message = 
aws_http_message_new_request(allocator);
+    ASSERT_NOT_NULL(head_message);
+    ASSERT_SUCCESS(aws_http_message_set_request_method(head_message, 
aws_byte_cursor_from_c_str("HEAD")));
+    ASSERT_SUCCESS(aws_http_message_set_request_path(head_message, 
aws_byte_cursor_from_c_str(destination_path)));
+    struct aws_http_headers *head_headers = 
aws_http_message_get_headers(head_message);
+    ASSERT_SUCCESS(aws_http_headers_add(head_headers, g_host_header_name, 
aws_byte_cursor_from_c_str(endpoint)));
+
+    struct aws_s3_meta_request_test_results head_results;
+    aws_s3_meta_request_test_results_init(&head_results, allocator);
+
+    struct aws_s3_tester_meta_request_options head_options = {
+        .allocator = allocator,
+        .meta_request_type = AWS_S3_META_REQUEST_TYPE_DEFAULT,
+        .message = head_message,
+        .validate_type = AWS_S3_TESTER_VALIDATE_TYPE_EXPECT_SUCCESS,
+        .default_type_options =
+            {
+                .mode = AWS_S3_TESTER_DEFAULT_TYPE_MODE_GET,
+                .operation_name = aws_byte_cursor_from_c_str("HeadObject"),
+            },
+    };
+
+    ASSERT_SUCCESS(aws_s3_tester_send_meta_request_with_options(&head_tester, 
&head_options, &head_results));
+    ASSERT_INT_EQUALS(AWS_HTTP_STATUS_CODE_200_OK, 
head_results.finished_response_status);
+
+    if (expect_metadata) {
+        struct aws_byte_cursor meta_value;
+        ASSERT_SUCCESS(aws_http_headers_get(
+            head_results.response_headers, 
aws_byte_cursor_from_c_str("x-amz-meta-test-key"), &meta_value));
+        ASSERT_TRUE(aws_byte_cursor_eq_c_str(&meta_value, "test-value"));
+
+        struct aws_byte_cursor cache_value;
+        ASSERT_SUCCESS(aws_http_headers_get(
+            head_results.response_headers, 
aws_byte_cursor_from_c_str("Cache-Control"), &cache_value));
+        ASSERT_TRUE(aws_byte_cursor_eq_c_str(&cache_value, "max-age=3600"));
+
+        struct aws_byte_cursor content_type_value;
+        ASSERT_SUCCESS(aws_http_headers_get(
+            head_results.response_headers, 
aws_byte_cursor_from_c_str("Content-Type"), &content_type_value));
+        ASSERT_TRUE(aws_byte_cursor_eq_c_str(&content_type_value, 
"application/x-replaced"));
+    } else {
+        /* Metadata NOT expected (documents MPU path limitation with COPY 
directive) */
+        struct aws_byte_cursor meta_value;
+        ASSERT_TRUE(
+            aws_http_headers_get(
+                head_results.response_headers, 
aws_byte_cursor_from_c_str("x-amz-meta-test-key"), &meta_value) !=
+            AWS_OP_SUCCESS);
+    }
+
+    aws_s3_meta_request_test_results_clean_up(&head_results);
+    aws_http_message_release(head_message);
+    aws_s3_tester_clean_up(&head_tester);
+
+    /* Verify tags */
+    if (expect_metadata) {
+        struct aws_s3_tester tag_tester;
+        AWS_ZERO_STRUCT(tag_tester);
+        ASSERT_SUCCESS(aws_s3_tester_init(allocator, &tag_tester));
+
+        struct aws_http_message *tag_message = 
aws_http_message_new_request(allocator);
+        ASSERT_NOT_NULL(tag_message);
+        ASSERT_SUCCESS(aws_http_message_set_request_method(tag_message, 
aws_byte_cursor_from_c_str("GET")));
+
+        char tag_path[1536];
+        snprintf(tag_path, sizeof(tag_path), "%s?tagging", destination_path);
+        ASSERT_SUCCESS(aws_http_message_set_request_path(tag_message, 
aws_byte_cursor_from_c_str(tag_path)));
+
+        struct aws_http_headers *tag_req_headers = 
aws_http_message_get_headers(tag_message);
+        ASSERT_SUCCESS(aws_http_headers_add(tag_req_headers, 
g_host_header_name, aws_byte_cursor_from_c_str(endpoint)));
+
+        struct aws_s3_meta_request_test_results tag_results;
+        aws_s3_meta_request_test_results_init(&tag_results, allocator);
+
+        struct aws_s3_tester_meta_request_options tag_options = {
+            .allocator = allocator,
+            .meta_request_type = AWS_S3_META_REQUEST_TYPE_DEFAULT,
+            .message = tag_message,
+            .validate_type = AWS_S3_TESTER_VALIDATE_TYPE_EXPECT_SUCCESS,
+            .default_type_options =
+                {
+                    .mode = AWS_S3_TESTER_DEFAULT_TYPE_MODE_GET,
+                    .operation_name = 
aws_byte_cursor_from_c_str("GetObjectTagging"),
+                },
+        };
+
+        
ASSERT_SUCCESS(aws_s3_tester_send_meta_request_with_options(&tag_tester, 
&tag_options, &tag_results));
+        ASSERT_INT_EQUALS(AWS_HTTP_STATUS_CODE_200_OK, 
tag_results.finished_response_status);
+        /* Body > 100 bytes = contains actual tags (empty TagSet is ~39 bytes) 
*/
+        ASSERT_TRUE(tag_results.received_body_size > 100);
+
+        aws_s3_meta_request_test_results_clean_up(&tag_results);
+        aws_http_message_release(tag_message);
+        aws_s3_tester_clean_up(&tag_tester);
+    }
+
+    return AWS_OP_SUCCESS;
+}
+
+AWS_TEST_CASE(
+    test_s3_copy_object_with_replace_metadata_and_tagging,
+    s_test_s3_copy_object_with_replace_metadata_and_tagging)
+static int s_test_s3_copy_object_with_replace_metadata_and_tagging(struct 
aws_allocator *allocator, void *ctx) {
+    (void)ctx;
+    /* REPLACE: copy with explicit metadata+tags, verify they arrive on 
destination */
+    ASSERT_SUCCESS(s_test_s3_copy_object_properties_helper(
+        allocator,
+        aws_byte_cursor_from_c_str("pre-existing-1MB"),
+        aws_byte_cursor_from_c_str("copies/destination_1MB_replace_props"),
+        true /* use_replace */,
+        true /* expect_metadata */));
+
+    /* COPY directive on the result: single-part CopyObject natively handles 
it */
+    ASSERT_SUCCESS(s_test_s3_copy_object_properties_helper(
+        allocator,
+        aws_byte_cursor_from_c_str("copies/destination_1MB_replace_props"),
+        aws_byte_cursor_from_c_str("copies/destination_1MB_copy_directive"),
+        false /* use_replace */,
+        true /* expect_metadata: single-part handles COPY natively */));
+
+    return AWS_OP_SUCCESS;
+}
+
+AWS_TEST_CASE(
+    test_s3_multipart_copy_object_with_replace_metadata_and_tagging,
+    s_test_s3_multipart_copy_object_with_replace_metadata_and_tagging)
+static int s_test_s3_multipart_copy_object_with_replace_metadata_and_tagging(
+    struct aws_allocator *allocator,
+    void *ctx) {
+    (void)ctx;
+    /* REPLACE: copy with explicit metadata+tags, verify they arrive on 
destination */
+    ASSERT_SUCCESS(s_test_s3_copy_object_properties_helper(
+        allocator,
+        aws_byte_cursor_from_c_str("pre-existing-2GB"),
+        aws_byte_cursor_from_c_str("copies/destination_2GB_replace_props"),
+        true /* use_replace */,
+        true /* expect_metadata */));
+
+    /* COPY directive on the result: MPU path does NOT copy metadata (known 
limitation) */
+    ASSERT_SUCCESS(s_test_s3_copy_object_properties_helper(
+        allocator,
+        aws_byte_cursor_from_c_str("copies/destination_2GB_replace_props"),
+        aws_byte_cursor_from_c_str("copies/destination_2GB_copy_directive"),
+        false /* use_replace */,
+        false /* expect_metadata: MPU path does NOT handle COPY directive */));
+
+    return AWS_OP_SUCCESS;
+}
+
 static int s_s3_get_object_mrap_helper(struct aws_allocator *allocator, bool 
multipart) {
     struct aws_s3_tester tester;
     AWS_ZERO_STRUCT(tester);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aws-c-s3-0.12.5/tests/s3_tester.c 
new/aws-c-s3-0.12.6/tests/s3_tester.c
--- old/aws-c-s3-0.12.5/tests/s3_tester.c       2026-06-02 23:10:16.000000000 
+0200
+++ new/aws-c-s3-0.12.6/tests/s3_tester.c       2026-06-11 20:12:01.000000000 
+0200
@@ -2371,6 +2371,15 @@
         goto error_clean_up_message;
     }
 
+    /* Copy Object request has a content length of 0 */
+    struct aws_http_header content_length_header = {
+        .name = g_content_length_header_name,
+        .value = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("0"),
+    };
+    if (aws_http_message_add_header(message, content_length_header)) {
+        goto error_clean_up_message;
+    }
+
     struct aws_byte_buf copy_source_value_encoded;
     aws_byte_buf_init(&copy_source_value_encoded, allocator, 1024);
     aws_byte_buf_append_encoding_uri_path(&copy_source_value_encoded, 
&x_amz_source);

Reply via email to