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]