Skip to content

Commit c5c3ca3

Browse files
authored
Fix SigV4 signing failure on bulk retry after partial success (#759)
* Fix SigV4 signing failure on bulk retry after partial success TrackingBytesArray.toInputStream() returned the entire underlying byte array including bytes of removed entries, while writeTo() correctly skipped them. This caused a mismatch between the x-amz-content-sha256 header (computed from toInputStream()) and the actual HTTP body (sent via writeTo()) when retrying a bulk request after partial success. Changed toInputStream() to build its result using the same entry traversal logic as writeTo(). Signed-off-by: Sotaro Hikita <bering1814@gmail.com> * Trigger CI Signed-off-by: Sotaro Hikita <bering1814@gmail.com> --------- Signed-off-by: Sotaro Hikita <bering1814@gmail.com>
1 parent f6973ef commit c5c3ca3

3 files changed

Lines changed: 43 additions & 2 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
4040
- Fixed serverless mode SaveMode.Overwrite failing when document count exceeds scroll size ([#693](https://github.com/opensearch-project/opensearch-hadoop/pull/693))
4141
- Fixed RowSerializationEventConverterTest for Spark 3.4+ StructType.toString() format change ([#702](https://github.com/opensearch-project/opensearch-hadoop/pull/702))
4242
- Fixed object fields with `enabled: false` returning empty structs or throwing exceptions when read via the connector ([#715](https://github.com/opensearch-project/opensearch-hadoop/pull/715))
43+
- Fixed SigV4 signing failure on bulk retry after partial success causing `x-amz-content-sha256 invalid` ([#759](https://github.com/opensearch-project/opensearch-hadoop/pull/759))
4344

4445
### Security
4546

mr/src/main/java/org/opensearch/hadoop/util/TrackingBytesArray.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
package org.opensearch.hadoop.util;
3030

3131
import java.io.ByteArrayInputStream;
32+
import java.io.ByteArrayOutputStream;
3233
import java.io.IOException;
3334
import java.io.InputStream;
3435
import java.io.OutputStream;
@@ -161,9 +162,14 @@ public void writeTo(OutputStream out) throws IOException {
161162
public InputStream toInputStream() {
162163
if (size == 0) {
163164
return new ByteArrayInputStream(new byte[0]);
164-
} else {
165-
return data.toInputStream();
166165
}
166+
ByteArrayOutputStream out = new ByteArrayOutputStream(size);
167+
try {
168+
writeTo(out);
169+
} catch (IOException e) {
170+
throw new RuntimeException(e);
171+
}
172+
return new ByteArrayInputStream(out.toByteArray());
167173
}
168174

169175
public void reset() {

mr/src/test/java/org/opensearch/hadoop/util/TrackingBytesArrayTest.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,4 +123,38 @@ public void testPopData() throws Exception {
123123
assertEquals(7, data.length());
124124
assertEquals(2, entry.length());
125125
}
126+
127+
@Test
128+
public void testToInputStreamIncludesRemovedEntriesBytesAfterRemove() throws Exception {
129+
TrackingBytesArray tba = new TrackingBytesArray(new BytesArray(1024));
130+
131+
byte[] entry1 = "{\"index\":{\"_id\":\"1\"}}\n{\"title\":\"doc1\"}\n".getBytes();
132+
byte[] entry2 = "{\"index\":{\"_id\":\"2\"}}\n{\"title\":\"doc2\"}\n".getBytes();
133+
byte[] entry3 = "{\"index\":{\"_id\":\"3\"}}\n{\"title\":\"doc3\"}\n".getBytes();
134+
135+
tba.copyFrom(new BytesArray(entry1, entry1.length));
136+
tba.copyFrom(new BytesArray(entry2, entry2.length));
137+
tba.copyFrom(new BytesArray(entry3, entry3.length));
138+
139+
// Simulate partial bulk success: first entry succeeded, remove it before retry
140+
tba.remove(0);
141+
142+
// writeTo: what gets sent over HTTP
143+
ByteArrayOutputStream httpBody = new ByteArrayOutputStream();
144+
tba.writeTo(httpBody);
145+
146+
// toInputStream: what SigV4 uses to compute x-amz-content-sha256
147+
byte[] signedBody = tba.toInputStream().readAllBytes();
148+
149+
// These SHOULD be equal for SigV4 signature to match the actual HTTP body.
150+
// If they differ, the signature computed from toInputStream() will not match
151+
// the body sent via writeTo(), causing AOSS to reject with
152+
// "x-amz-content-sha256 invalid"
153+
assertArrayEquals(
154+
"toInputStream() must return the same bytes as writeTo() after remove(). " +
155+
"Mismatch causes SigV4 signature failure on AOSS bulk retry.",
156+
httpBody.toByteArray(),
157+
signedBody
158+
);
159+
}
126160
}

0 commit comments

Comments
 (0)