Skip to content

Add RequestBody.fromRemainingByteBuffer() #1569

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 3, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changes/next-release/feature-AWSSDKforJavav2-e8d7ec5.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"type": "feature",
"category": "AWS SDK for Java v2",
"description": "Add `RequestBody.fromRemainingByteBuffer(ByteBuffer)` that copies only the remaining readable bytes of the buffer. See [#1534](https://github.com/aws/aws-sdk-java-v2/issues/1534)"
}
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,9 @@ public static RequestBody fromBytes(byte[] bytes) {
/**
* Creates a {@link RequestBody} from a {@link ByteBuffer}. Buffer contents are copied so any modifications
* made to the original {@link ByteBuffer} are not reflected in the {@link RequestBody}.
* <p>
* <b>NOTE:</b> This method always copies the entire contents of the buffer, ignoring the current read position. Use
* {@link #fromRemainingByteBuffer(ByteBuffer)} if you need it to copy only the remaining readable bytes.
*
* @param byteBuffer ByteBuffer to send to the service.
* @return RequestBody instance.
Expand All @@ -169,6 +172,18 @@ public static RequestBody fromByteBuffer(ByteBuffer byteBuffer) {
return fromBytesDirect(BinaryUtils.copyAllBytesFrom(byteBuffer));
}

/**
* Creates a {@link RequestBody} from the remaining readable bytes from a {@link ByteBuffer}. Unlike
* {@link #fromByteBuffer(ByteBuffer)}, this method respects the current read position of the buffer and reads only
* the remaining bytes. The buffer is copied before reading so no changes are made to original buffer.
*
* @param byteBuffer ByteBuffer to send to the service.
* @return RequestBody instance.
*/
public static RequestBody fromRemainingByteBuffer(ByteBuffer byteBuffer) {
return fromBytesDirect(BinaryUtils.copyRemainingBytesFrom(byteBuffer));
}

/**
* Creates a {@link RequestBody} with no content.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
import com.google.common.jimfs.Configuration;
import com.google.common.jimfs.Jimfs;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
Expand Down Expand Up @@ -104,4 +103,23 @@ public void emptyBytesConstructorHasCorrectContentType() {
RequestBody requestBody = RequestBody.empty();
assertThat(requestBody.contentType()).isEqualTo(Mimetype.MIMETYPE_OCTET_STREAM);
}

@Test
public void remainingByteBufferConstructorOnlyRemainingBytesCopied() throws IOException {
ByteBuffer bb = ByteBuffer.allocate(4);
bb.put(new byte[]{1, 2, 3, 4});
bb.flip();

bb.get();
bb.get();

int originalRemaining = bb.remaining();

RequestBody requestBody = RequestBody.fromRemainingByteBuffer(bb);

assertThat(requestBody.contentLength()).isEqualTo(originalRemaining);

byte[] requestBodyBytes = IoUtils.toByteArray(requestBody.contentStreamProvider().newStream());
assertThat(ByteBuffer.wrap(requestBodyBytes)).isEqualTo(bb);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
*/
@SdkProtectedApi
public final class BinaryUtils {
private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];

private BinaryUtils() {
}
Expand Down Expand Up @@ -150,6 +151,29 @@ public static byte[] copyAllBytesFrom(ByteBuffer bb) {
return dst;
}

public static byte[] copyRemainingBytesFrom(ByteBuffer bb) {
if (bb == null) {
return null;
}

if (!bb.hasRemaining()) {
return EMPTY_BYTE_ARRAY;
}

if (bb.hasArray()) {
int endIdx = bb.arrayOffset() + bb.limit();
int startIdx = endIdx - bb.remaining();
return Arrays.copyOfRange(bb.array(), startIdx, endIdx);
}

ByteBuffer copy = bb.asReadOnlyBuffer();

byte[] dst = new byte[copy.remaining()];
copy.get(dst);

return dst;
}

/**
* Returns a copy of the bytes from the given <code>ByteBuffer</code>,
* ranging from the the buffer's current position to the buffer's limit; or
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

package software.amazon.awssdk.utils;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
Expand Down Expand Up @@ -159,4 +160,45 @@ public void testCopyBytesFrom_DirectByteBuffer_Idempotent() {
assertTrue(partial1.length == 3);
assertTrue(Arrays.equals(new byte[] {2, 3, 4}, partial1));
}

@Test
public void testCopyRemainingBytesFrom_nullBuffer() {
assertThat(BinaryUtils.copyRemainingBytesFrom(null)).isNull();
}

@Test
public void testCopyRemainingBytesFrom_noRemainingBytes() {
ByteBuffer bb = ByteBuffer.allocate(1);
bb.put(new byte[]{1});
bb.flip();

bb.get();

assertThat(BinaryUtils.copyRemainingBytesFrom(bb)).hasSize(0);
}

@Test
public void testCopyRemainingBytesFrom_fullBuffer() {
ByteBuffer bb = ByteBuffer.allocate(4);
bb.put(new byte[]{1, 2, 3, 4});
bb.flip();

byte[] copy = BinaryUtils.copyRemainingBytesFrom(bb);
assertThat(bb).isEqualTo(ByteBuffer.wrap(copy));
assertThat(copy).hasSize(4);
}

@Test
public void testCopyRemainingBytesFrom_partiallyReadBuffer() {
ByteBuffer bb = ByteBuffer.allocate(4);
bb.put(new byte[]{1, 2, 3, 4});
bb.flip();

bb.get();
bb.get();

byte[] copy = BinaryUtils.copyRemainingBytesFrom(bb);
assertThat(bb).isEqualTo(ByteBuffer.wrap(copy));
assertThat(copy).hasSize(2);
}
}