Skip to content

Commit d7a1f8e

Browse files
committed
Fix large payload bugs in ByteArrayBodyGenerator.
Fixes three bugs exposed when a ByteArrayBodyGenerator is used to pass a POST body to the AsyncHttpProvider that is larger than the AsyncHttpProvider's BodyChunkedInput buffer size. Found when using the Netty provider to pass a 50k+ POST body. A read from a ByteBody having more content than the buffer could hold would return a full read buffer but incorrectly compute the internal pointer for the remaining content by subtracting the read length from the total content size rather than adding it to the current read position. A subsequent read from the same ByteBody would result in a java.lang.IndexOutOfBoundsException due to improperly passing the total length of the current body as the length argument to ByteBuffer.put() rather than the number of bytes to read from the offset. ByteArrayBodyGenerator.ByteBody also incorrectly implemented the Body interface by returning the total body length for each non-EOF read rather than the actual number of bytes read. All three of these bugs would not be seen unless the body size was greater than the capacity of a single buffer.
1 parent 9e5e4ec commit d7a1f8e

File tree

2 files changed

+84
-4
lines changed

2 files changed

+84
-4
lines changed

src/main/java/com/ning/http/client/generators/ByteArrayBodyGenerator.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,16 @@ public long read(ByteBuffer byteBuffer) throws IOException {
4343
return -1;
4444
}
4545

46-
if (bytes.length - lastPosition <= byteBuffer.capacity()) {
47-
byteBuffer.put(bytes, lastPosition, bytes.length);
46+
final int remaining = bytes.length - lastPosition;
47+
if (remaining <= byteBuffer.capacity()) {
48+
byteBuffer.put(bytes, lastPosition, remaining);
4849
eof = true;
50+
return remaining;
4951
} else {
5052
byteBuffer.put(bytes, lastPosition, byteBuffer.capacity());
51-
lastPosition = bytes.length - byteBuffer.capacity();
53+
lastPosition = lastPosition + byteBuffer.capacity();
54+
return byteBuffer.capacity();
5255
}
53-
return bytes.length;
5456
}
5557

5658
public void close() throws IOException {
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*
2+
* Copyright (c) 2010-2012 Sonatype, Inc. All rights reserved.
3+
*
4+
* This program is licensed to you under the Apache License Version 2.0,
5+
* and you may not use this file except in compliance with the Apache License Version 2.0.
6+
* You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0.
7+
*
8+
* Unless required by applicable law or agreed to in writing,
9+
* software distributed under the Apache License Version 2.0 is distributed on an
10+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
* See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
12+
*/
13+
14+
package com.ning.http.client.generators;
15+
16+
import com.ning.http.client.Body;
17+
18+
import org.testng.annotations.Test;
19+
20+
import java.io.IOException;
21+
import java.nio.ByteBuffer;
22+
import java.util.Random;
23+
24+
import static org.testng.Assert.assertEquals;
25+
import static org.testng.Assert.assertTrue;
26+
27+
/**
28+
* @author Bryan Davis [email protected]
29+
*/
30+
public class ByteArrayBodyGeneratorTest {
31+
32+
private final Random random = new Random();
33+
private final int chunkSize = 1024 * 8;
34+
35+
@Test(groups = "standalone")
36+
public void testSingleRead() throws IOException {
37+
final int srcArraySize = chunkSize - 1;
38+
final byte[] srcArray = new byte[srcArraySize];
39+
random.nextBytes(srcArray);
40+
41+
final ByteArrayBodyGenerator babGen =
42+
new ByteArrayBodyGenerator(srcArray);
43+
final Body body = babGen.createBody();
44+
45+
final ByteBuffer chunkBuffer = ByteBuffer.allocate(chunkSize);
46+
47+
// should take 1 read to get through the srcArray
48+
assertEquals(body.read(chunkBuffer), srcArraySize);
49+
assertEquals(chunkBuffer.position(), srcArraySize, "bytes read");
50+
chunkBuffer.clear();
51+
52+
assertEquals(body.read(chunkBuffer), -1, "body at EOF");
53+
}
54+
55+
@Test(groups = "standalone")
56+
public void testMultipleReads() throws IOException {
57+
final int srcArraySize = (3 * chunkSize) + 42;
58+
final byte[] srcArray = new byte[srcArraySize];
59+
random.nextBytes(srcArray);
60+
61+
final ByteArrayBodyGenerator babGen =
62+
new ByteArrayBodyGenerator(srcArray);
63+
final Body body = babGen.createBody();
64+
65+
final ByteBuffer chunkBuffer = ByteBuffer.allocate(chunkSize);
66+
67+
int reads = 0;
68+
int bytesRead = 0;
69+
while (body.read(chunkBuffer) != -1) {
70+
reads += 1;
71+
bytesRead += chunkBuffer.position();
72+
chunkBuffer.clear();
73+
}
74+
assertEquals(reads, 4, "reads to drain generator");
75+
assertEquals(bytesRead, srcArraySize, "bytes read");
76+
}
77+
78+
}

0 commit comments

Comments
 (0)