Skip to content

Commit 647555c

Browse files
committed
Support 64-bit chunk headers
git-svn-id: https://svn.apache.org/repos/asf/httpcomponents/httpcore/branches/4.4.x@1737928 13f79535-47bb-0310-9956-ffa450edef68
1 parent e57da70 commit 647555c

File tree

4 files changed

+63
-28
lines changed

4 files changed

+63
-28
lines changed

httpcore-nio/src/main/java/org/apache/http/impl/nio/codecs/ChunkDecoder.java

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,8 @@ public class ChunkDecoder extends AbstractContentDecoder {
6565
private boolean endOfStream;
6666

6767
private CharArrayBuffer lineBuf;
68-
private int chunkSize;
69-
private int pos;
68+
private long chunkSize;
69+
private long pos;
7070

7171
private final MessageConstraints constraints;
7272
private final List<CharArrayBuffer> trailerBufs;
@@ -83,8 +83,8 @@ public ChunkDecoder(
8383
final HttpTransportMetricsImpl metrics) {
8484
super(channel, buffer, metrics);
8585
this.state = READ_CONTENT;
86-
this.chunkSize = -1;
87-
this.pos = 0;
86+
this.chunkSize = -1L;
87+
this.pos = 0L;
8888
this.endOfChunk = false;
8989
this.endOfStream = false;
9090
this.constraints = constraints != null ? constraints : MessageConstraints.DEFAULT;
@@ -129,13 +129,13 @@ private void readChunkHead() throws IOException {
129129
if (separator < 0) {
130130
separator = this.lineBuf.length();
131131
}
132+
final String s = this.lineBuf.substringTrimmed(0, separator);
132133
try {
133-
final String s = this.lineBuf.substringTrimmed(0, separator);
134-
this.chunkSize = Integer.parseInt(s, 16);
134+
this.chunkSize = Long.parseLong(s, 16);
135135
} catch (final NumberFormatException e) {
136-
throw new MalformedChunkCodingException("Bad chunk header");
136+
throw new MalformedChunkCodingException("Bad chunk header: " + s);
137137
}
138-
this.pos = 0;
138+
this.pos = 0L;
139139
} else if (this.endOfStream) {
140140
throw new ConnectionClosedException("Premature end of chunk coded message body: " +
141141
"closing chunk expected");
@@ -193,7 +193,7 @@ public int read(final ByteBuffer dst) throws IOException {
193193
int totalRead = 0;
194194
while (this.state != COMPLETED) {
195195

196-
if (!this.buffer.hasData() || this.chunkSize == -1) {
196+
if (!this.buffer.hasData() || this.chunkSize == -1L) {
197197
final int bytesRead = fillBufferFromChannel();
198198
if (bytesRead == -1) {
199199
this.endOfStream = true;
@@ -203,21 +203,21 @@ public int read(final ByteBuffer dst) throws IOException {
203203
switch (this.state) {
204204
case READ_CONTENT:
205205

206-
if (this.chunkSize == -1) {
206+
if (this.chunkSize == -1L) {
207207
readChunkHead();
208-
if (this.chunkSize == -1) {
208+
if (this.chunkSize == -1L) {
209209
// Unable to read a chunk head
210210
return totalRead;
211211
}
212-
if (this.chunkSize == 0) {
212+
if (this.chunkSize == 0L) {
213213
// Last chunk. Read footers
214-
this.chunkSize = -1;
214+
this.chunkSize = -1L;
215215
this.state = READ_FOOTERS;
216216
break;
217217
}
218218
}
219-
final int maxLen = this.chunkSize - this.pos;
220-
final int len = this.buffer.read(dst, maxLen);
219+
final long maxLen = this.chunkSize - this.pos;
220+
final int len = this.buffer.read(dst, (int) Math.min(maxLen, Integer.MAX_VALUE));
221221
if (len > 0) {
222222
this.pos += len;
223223
totalRead += len;
@@ -233,8 +233,8 @@ public int read(final ByteBuffer dst) throws IOException {
233233

234234
if (this.pos == this.chunkSize) {
235235
// At the end of the chunk
236-
this.chunkSize = -1;
237-
this.pos = 0;
236+
this.chunkSize = -1L;
237+
this.pos = 0L;
238238
this.endOfChunk = true;
239239
break;
240240
}

httpcore-nio/src/test/java/org/apache/http/impl/nio/codecs/TestChunkDecoder.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -586,4 +586,24 @@ public void testInvalidInput() throws Exception {
586586
decoder.read(null);
587587
}
588588

589+
@Test
590+
public void testHugeChunk() throws Exception {
591+
final String s = "1234567890abcdef\r\n0123456789abcdef";
592+
final ReadableByteChannel channel = new ReadableByteChannelMock(
593+
new String[] {s}, Consts.ASCII);
594+
final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, Consts.ASCII);
595+
final HttpTransportMetricsImpl metrics = new HttpTransportMetricsImpl();
596+
final ChunkDecoder decoder = new ChunkDecoder(channel, inbuf, metrics);
597+
598+
final ByteBuffer dst = ByteBuffer.allocate(4);
599+
600+
int bytesRead = decoder.read(dst);
601+
Assert.assertEquals(4, bytesRead);
602+
Assert.assertEquals("0123", CodecTestUtils.convert(dst));
603+
dst.clear();
604+
bytesRead = decoder.read(dst);
605+
Assert.assertEquals(4, bytesRead);
606+
Assert.assertEquals("4567", CodecTestUtils.convert(dst));
607+
}
608+
589609
}

httpcore/src/main/java/org/apache/http/impl/io/ChunkedInputStream.java

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,10 @@ public class ChunkedInputStream extends InputStream {
7676
private int state;
7777

7878
/** The chunk size */
79-
private int chunkSize;
79+
private long chunkSize;
8080

8181
/** The current position within the current chunk */
82-
private int pos;
82+
private long pos;
8383

8484
/** True if we've reached the end of stream */
8585
private boolean eof = false;
@@ -101,7 +101,7 @@ public class ChunkedInputStream extends InputStream {
101101
public ChunkedInputStream(final SessionInputBuffer in, final MessageConstraints constraints) {
102102
super();
103103
this.in = Args.notNull(in, "Session input buffer");
104-
this.pos = 0;
104+
this.pos = 0L;
105105
this.buffer = new CharArrayBuffer(16);
106106
this.constraints = constraints != null ? constraints : MessageConstraints.DEFAULT;
107107
this.state = CHUNK_LEN;
@@ -120,7 +120,7 @@ public ChunkedInputStream(final SessionInputBuffer in) {
120120
public int available() throws IOException {
121121
if (this.in instanceof BufferInfo) {
122122
final int len = ((BufferInfo) this.in).length();
123-
return Math.min(len, this.chunkSize - this.pos);
123+
return (int) Math.min(len, this.chunkSize - this.pos);
124124
} else {
125125
return 0;
126126
}
@@ -188,7 +188,7 @@ public int read (final byte[] b, final int off, final int len) throws IOExceptio
188188
return -1;
189189
}
190190
}
191-
final int bytesRead = in.read(b, off, Math.min(len, chunkSize - pos));
191+
final int bytesRead = in.read(b, off, (int) Math.min(len, chunkSize - pos));
192192
if (bytesRead != -1) {
193193
pos += bytesRead;
194194
if (pos >= chunkSize) {
@@ -225,12 +225,12 @@ private void nextChunk() throws IOException {
225225
}
226226
try {
227227
chunkSize = getChunkSize();
228-
if (chunkSize < 0) {
228+
if (chunkSize < 0L) {
229229
throw new MalformedChunkCodingException("Negative chunk size");
230230
}
231231
state = CHUNK_DATA;
232-
pos = 0;
233-
if (chunkSize == 0) {
232+
pos = 0L;
233+
if (chunkSize == 0L) {
234234
eof = true;
235235
parseTrailerHeaders();
236236
}
@@ -245,7 +245,7 @@ private void nextChunk() throws IOException {
245245
* comments after a semicolon. The line must end with a CRLF: "a3; some
246246
* comment\r\n" Positions the stream at the start of the next line.
247247
*/
248-
private int getChunkSize() throws IOException {
248+
private long getChunkSize() throws IOException {
249249
final int st = this.state;
250250
switch (st) {
251251
case CHUNK_CRLF:
@@ -272,10 +272,11 @@ private int getChunkSize() throws IOException {
272272
if (separator < 0) {
273273
separator = this.buffer.length();
274274
}
275+
final String s = this.buffer.substringTrimmed(0, separator);
275276
try {
276-
return Integer.parseInt(this.buffer.substringTrimmed(0, separator), 16);
277+
return Long.parseLong(s, 16);
277278
} catch (final NumberFormatException e) {
278-
throw new MalformedChunkCodingException("Bad chunk header");
279+
throw new MalformedChunkCodingException("Bad chunk header: " + s);
279280
}
280281
default:
281282
throw new IllegalStateException("Inconsistent codec state");

httpcore/src/test/java/org/apache/http/impl/io/TestChunkCoding.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -506,5 +506,19 @@ public void testResumeOnSocketTimeoutInChunk() throws IOException {
506506
in.close();
507507
}
508508

509+
// Test for when buffer is larger than chunk size
510+
@Test
511+
public void testHugeChunk() throws IOException {
512+
final ChunkedInputStream in = new ChunkedInputStream(
513+
new SessionInputBufferMock("1234567890abcdef\r\n01234567", Consts.ISO_8859_1));
514+
final ByteArrayOutputStream out = new ByteArrayOutputStream();
515+
for (int i = 0; i < 8; ++i) {
516+
out.write(in.read());
517+
}
518+
519+
final String result = new String(out.toByteArray(), Consts.ISO_8859_1);
520+
Assert.assertEquals("01234567", result);
521+
}
522+
509523
}
510524

0 commit comments

Comments
 (0)