Skip to content

Commit 79ed59f

Browse files
committed
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>
1 parent 525183e commit 79ed59f

2 files changed

Lines changed: 42 additions & 2 deletions

File tree

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)