smaheshwar-pltr commented on code in PR #4039:
URL: https://github.com/apache/iceberg-python/pull/4039#discussion_r4151681116


##########
pyiceberg/io/fsspec.py:
##########
@@ -148,6 +148,9 @@ def __call__(self, request: "AWSRequest", **_: Any) -> None:
             "uri": request.url,
             "headers": {key: [val] for key, val in request.headers.items()},
         }
+        if request.method == "POST" and "delete" in 
parse_qs(urlparse(request.url).query, keep_blank_values=True):

Review Comment:
   This matches the [Java client's existing predicate and body 
handling](https://github.com/apache/iceberg/blob/d008ad230bed30383a667af8151573e48de7624c/aws/src/main/java/org/apache/iceberg/aws/s3/signer/S3V4RestSignerClient.java#L349-L367):
 POST with a `delete` query key. The [REST 
contract](https://github.com/apache/iceberg/blob/d008ad230bed30383a667af8151573e48de7624c/open-api/rest-catalog-open-api.yaml#L5834-L5838)
 calls out DeleteObjects because its body contains the keys that the signing 
service must validate; the URL identifies only the bucket. 
`keep_blank_values=True` makes both `?delete` and `?delete=` recognizable. 
Other POST operations, including multipart completion, keep their existing 
behavior.



##########
pyiceberg/io/fsspec.py:
##########
@@ -148,6 +148,9 @@ def __call__(self, request: "AWSRequest", **_: Any) -> None:
             "uri": request.url,
             "headers": {key: [val] for key, val in request.headers.items()},
         }
+        if request.method == "POST" and "delete" in 
parse_qs(urlparse(request.url).query, keep_blank_values=True):
+            if body := request.body:
+                signer_body["body"] = body.decode("utf-8")

Review Comment:
   The bytes assumption comes from [Botocore's REST XML 
serializer](https://github.com/boto/botocore/9fda087a19c80ad04bff404fc463571ec8a9ec3f/botocore/serialize.py#L1107-L1115),
 which serializes the request using its [UTF-8 
default](https://github.com/boto/botocore/9fda087a19c80ad04bff404fc463571ec8a9ec3f/botocore/serialize.py#L106-L110).
 
[AWSRequest.body](https://github.com/boto/botocore/9fda087a19c80ad04bff404fc463571ec8a9ec3f/botocore/awsrequest.py#L483-L488)
 exposes that prepared body. Decoding creates the string expected by the 
signing API; it does not replace the original XML bytes or checksum headers 
sent to S3. The guards above prevent accessing unrelated upload streams.



##########
tests/io/test_fsspec.py:
##########
@@ -995,6 +996,49 @@ def test_s3v4_rest_signer(requests_mock: Mocker) -> None:
     }
 
 
[email protected]("query", ["delete", "delete=", 
"delete=&x-id=DeleteObjects"])
+def test_s3v4_rest_signer_sends_delete_objects_body(requests_mock: Mocker, 
query: str) -> None:

Review Comment:
   This is reachable even when deleting one file: [FsspecFileIO.delete calls 
fs.rm](https://github.com/apache/iceberg-python/blob/5d96c0e910abbb14ebc15deeb0ffe1b2814e038e/pyiceberg/io/fsspec.py#L479-L495),
 and [s3fs batches those paths through 
_bulk_delete](https://github.com/fsspec/s3fs/blob/65f394575b9667f33b59473dc28a8f1cf6708745/s3fs/core.py#L2099-L2116),
 which [calls 
DeleteObjects](https://github.com/fsspec/s3fs/blob/65f394575b9667f33b59473dc28a8f1cf6708745/s3fs/core.py#L2077-L2085).
 All three cases here failed before the fix because the captured signing JSON 
had no `body`. The six cases below separately guard against forwarding or 
reading unrelated request bodies.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to