Ritesh Shukla created HDDS-17639:
------------------------------------

             Summary: S3G: nine small S3 compatibility and robustness gaps 
found while testing request handling against AWS S3
                 Key: HDDS-17639
                 URL: https://issues.apache.org/jira/browse/HDDS-17639
             Project: Apache Ozone
          Issue Type: Improvement
          Components: S3
            Reporter: Ritesh Shukla
            Assignee: Ritesh Shukla


h3. Summary

While testing how the S3 Gateway reads request bodies and query parameters 
against AWS S3 (HDDS-17600 and its follow-ups HDDS-17629 to HDDS-17632), we 
found nine smaller problems. They are collected here so they can be fixed 
together or split out. "Run" means measured, on a cluster or in the gateway's 
unit-test harness; "read" means found in the code only. "Tested on S3" means 
the request was sent to AWS S3 on 2026-10-31. Line numbers are for master 
{{ef63bacef7}}.

h3. Requests answered differently from S3, or with a server error

* *A {{Part}} with no {{ETag}} gets HTTP 500.* A CompleteMultipartUpload body 
with {{<Part><PartNumber>1</PartNumber></Part>}} passes a null ETag to the 
protobuf builder's {{setPartName}} in 
{{OmMultipartUploadCompleteList.getPartsList}}, which throws a 
NullPointerException; the client gets Jetty's HTML error page with status 500 
(run, mini cluster). S3's answer was not tested; 400 {{MalformedXML}} is likely.
* *An empty {{Key}} is sent to OM.* For a DeleteObjects entry whose key is 
empty, AWS S3 answers 400 {{UserKeyMustBeSpecified}} (tested on S3 with 
{{<Key><a/></Key>}}, which S3 reads as an empty key). The gateway sends the 
empty name to OM and answers 200 with an empty {{Deleted}} entry (run). The 
gateway has no {{UserKeyMustBeSpecified}} error code.
* *Elements S3 does not define are ignored.* AWS S3 refuses an element its 
schema does not define, even an empty one, with 400 {{MalformedXML}}: 
{{<Foo/>}} or {{<Foo>x</Foo>}} inside {{Delete}}, {{Object}} or {{Part}} 
(tested on S3). The gateway ignores them and acts on the rest of the body 
(run). A fix must keep accepting elements S3 defines but the gateway does not 
model, such as the checksum elements of a {{Part}} and the {{URI}} of a group 
grantee in an ACL body.
* *An empty {{part-number-marker}} gets HTTP 500.* {{GET 
/bucket/key?uploadId=<id>&part-number-marker=}} (ListParts) calls 
{{Integer.parseInt("")}} ({{EndpointBase.java:868-874}}, from 
{{MultipartKeyHandler.java:63}}). The NumberFormatException is not an S3 error, 
so the client gets HTTP 500 (run, mini cluster). S3's answer was not tested; 
400 {{InvalidArgument}} is likely.
* *{{HEAD ?uploadId=}} and {{GET ?partNumber=1&uploadId=}}.* AWS S3 answers 
both with 405 {{MethodNotAllowed}} (tested on S3). The gateway answers the 
first as HeadObject (200) and the second as ListParts (404 {{NoSuchUpload}}) 
(run). Both are reads; nothing is changed.
* *An ACL grantee read as {{CanonicalUser}} with no {{ID}} gets a server 
error.* A PutBucketAcl grantee whose type attribute binds the XML Schema 
instance namespace to a prefix other than {{xsi}}, such as {{i:type="Group"}} 
or {{i:type="AmazonCustomerByEmail"}}, is read as a CanonicalUser grantee 
without an ID. {{OzoneAcl.validateNameAndType}} then throws a 
NullPointerException ({{OzoneAcl.java:145}}, reached from {{S3Acl.java:224}}), 
and the request fails with a server error (run, unit harness). A grantee 
written with {{xsi:type="CanonicalUser"}} and no {{ID}} reaches the same line 
(read). S3's answer to these bodies was not tested.

h3. Inside the gateway

* *A failed part upload is counted twice in the failure metrics.* One failed 
UploadPart adds 1 to the part-upload failure counter and 1 to the create-key 
failure counter; one failed UploadPartCopy adds 1 to the copy failure counter 
and 1 to the create-key failure counter (run, unit harness). 
{{createMultipartKey}} counts its failure ({{ObjectEndpoint.java:1115-1118}} 
and {{1131-1134}}) and rethrows, and the outer catch in {{handlePutRequest}} 
counts it again ({{ObjectEndpoint.java:343-348}}).
* *Lifecycle builds a new JAXB context for every request.* 
{{BucketLifecycleHandler}} creates a new 
{{PutBucketLifecycleConfigurationUnmarshaller}} for each 
PutBucketLifecycleConfiguration ({{BucketLifecycleHandler.java:137}}), and its 
constructor builds a {{JAXBContext}} (read). The other request-body readers are 
built once and reused.
* *The unit-test client stub is looser than OM.* {{OzoneBucketStub}} keeps a 
deleted key's bytes; its {{listParts}}, {{completeMultipartUpload}} and 
{{abortMultipartUpload}} check only that some upload exists on the key, not the 
upload ID; {{createMultipartKey}} accepts any part number, including 0 and 
numbers above 10,000; {{listParts}} hides part 0; and 
{{completeMultipartUpload}} answers {{InvalidPartOrder}} for part numbers at or 
below 0, where OM answers {{InvalidPart}} (read). Gateway unit tests can 
therefore pass against the stub for requests a real cluster refuses. Test code 
only.

h3. Suggested handling

Most items are small and independent: the missing ETag, the empty key, the 
empty part-number-marker, the HEAD and GET answers, the metrics, the 
per-request JAXB context and the stub. Two need more care. The ACL item touches 
the same code as the ACL parts of HDDS-17632 and is best done with them. 
Unknown elements need a list of the elements S3 defines for each request, so 
that refusing junk does not refuse fields newer SDKs send.

----
🤖 Generated with [Claude Code|https://claude.com/claude-code]



--
This message was sent by Atlassian Jira
(v10.3.26#10030026)

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

Reply via email to