You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hc.apache.org by ol...@apache.org on 2014/02/07 14:28:03 UTC
svn commit: r1565641 - in /httpcomponents/httpcore/trunk: ./
httpcore-nio/src/main/java/org/apache/http/impl/nio/codecs/
httpcore-nio/src/test/java/org/apache/http/impl/nio/codecs/
httpcore/src/main/java/org/apache/http/impl/io/ httpcore/src/test/java/...
Author: olegk
Date: Fri Feb 7 13:28:02 2014
New Revision: 1565641
URL: http://svn.apache.org/r1565641
Log:
HTTPCORE-372: Chunk decoders to throw an I/O exception if data stream is terminated without a closing chunk
Contributed by Dmitry Potapov <potapov.d at gmail.com>
Modified:
httpcomponents/httpcore/trunk/RELEASE_NOTES.txt
httpcomponents/httpcore/trunk/httpcore-nio/src/main/java/org/apache/http/impl/nio/codecs/ChunkDecoder.java
httpcomponents/httpcore/trunk/httpcore-nio/src/test/java/org/apache/http/impl/nio/codecs/TestChunkDecoder.java
httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/impl/io/ChunkedInputStream.java
httpcomponents/httpcore/trunk/httpcore/src/test/java/org/apache/http/impl/io/TestChunkCoding.java
Modified: httpcomponents/httpcore/trunk/RELEASE_NOTES.txt
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/RELEASE_NOTES.txt?rev=1565641&r1=1565640&r2=1565641&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/RELEASE_NOTES.txt (original)
+++ httpcomponents/httpcore/trunk/RELEASE_NOTES.txt Fri Feb 7 13:28:02 2014
@@ -1,6 +1,10 @@
-Changes since release 4.3.1
+Changes for release 4.4-alpha1
-------------------
+* [HTTPCORE-372] Blocking and non-blocking chunk decoders to throw an I/O exception if data stream
+ is terminated without a closing chunk.
+ Contributed by Dmitry Potapov <potapov.d at gmail.com>
+
* [HTTPCORE-368] Customizable buffer management strategies for SSLIOSession.
Contributed by offbynull <offbynull at gmail.com>
Modified: httpcomponents/httpcore/trunk/httpcore-nio/src/main/java/org/apache/http/impl/nio/codecs/ChunkDecoder.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore-nio/src/main/java/org/apache/http/impl/nio/codecs/ChunkDecoder.java?rev=1565641&r1=1565640&r2=1565641&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore-nio/src/main/java/org/apache/http/impl/nio/codecs/ChunkDecoder.java (original)
+++ httpcomponents/httpcore/trunk/httpcore-nio/src/main/java/org/apache/http/impl/nio/codecs/ChunkDecoder.java Fri Feb 7 13:28:02 2014
@@ -33,6 +33,7 @@ import java.nio.channels.ReadableByteCha
import java.util.ArrayList;
import java.util.List;
+import org.apache.http.ConnectionClosedException;
import org.apache.http.Header;
import org.apache.http.MalformedChunkCodingException;
import org.apache.http.ParseException;
@@ -94,7 +95,7 @@ public class ChunkDecoder extends Abstra
throw new MalformedChunkCodingException("CRLF expected at end of chunk");
}
} else {
- if (this.buffer.length() > 2) {
+ if (this.buffer.length() > 2 || this.endOfStream) {
throw new MalformedChunkCodingException("CRLF expected at end of chunk");
}
return;
@@ -113,6 +114,9 @@ public class ChunkDecoder extends Abstra
throw new MalformedChunkCodingException("Bad chunk header");
}
this.pos = 0;
+ } else if (this.endOfStream) {
+ throw new ConnectionClosedException("Premature end of chunk coded message body: " +
+ "closing chunk expected");
}
}
@@ -177,10 +181,6 @@ public class ChunkDecoder extends Abstra
readChunkHead();
if (this.chunkSize == -1) {
// Unable to read a chunk head
- if (this.endOfStream) {
- this.state = COMPLETED;
- this.completed = true;
- }
return totalRead;
}
if (this.chunkSize == 0) {
Modified: httpcomponents/httpcore/trunk/httpcore-nio/src/test/java/org/apache/http/impl/nio/codecs/TestChunkDecoder.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore-nio/src/test/java/org/apache/http/impl/nio/codecs/TestChunkDecoder.java?rev=1565641&r1=1565640&r2=1565641&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore-nio/src/test/java/org/apache/http/impl/nio/codecs/TestChunkDecoder.java (original)
+++ httpcomponents/httpcore/trunk/httpcore-nio/src/test/java/org/apache/http/impl/nio/codecs/TestChunkDecoder.java Fri Feb 7 13:28:02 2014
@@ -31,10 +31,12 @@ import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;
+import org.apache.http.ConnectionClosedException;
import org.apache.http.Consts;
import org.apache.http.Header;
import org.apache.http.MalformedChunkCodingException;
import org.apache.http.ReadableByteChannelMock;
+import org.apache.http.TruncatedChunkException;
import org.apache.http.impl.io.HttpTransportMetricsImpl;
import org.apache.http.impl.nio.reactor.SessionInputBufferImpl;
import org.apache.http.nio.reactor.SessionInputBuffer;
@@ -211,7 +213,7 @@ public class TestChunkDecoder {
Assert.assertTrue(decoder.isCompleted());
}
- @Test
+ @Test(expected=MalformedChunkCodingException.class)
public void testMalformedChunkSizeDecoding() throws Exception {
final String s = "5\r\n01234\r\n5zz\r\n56789\r\n6\r\nabcdef\r\n0\r\n\r\n";
final ReadableByteChannel channel = new ReadableByteChannelMock(
@@ -222,16 +224,10 @@ public class TestChunkDecoder {
final ChunkDecoder decoder = new ChunkDecoder(channel, inbuf, metrics);
final ByteBuffer dst = ByteBuffer.allocate(1024);
-
- try {
- decoder.read(dst);
- Assert.fail("MalformedChunkCodingException should have been thrown");
- } catch (final MalformedChunkCodingException ex) {
- // expected
- }
+ decoder.read(dst);
}
- @Test
+ @Test(expected=MalformedChunkCodingException.class)
public void testMalformedChunkEndingDecoding() throws Exception {
final String s = "5\r\n01234\r\n5\r\n56789\r\r6\r\nabcdef\r\n0\r\n\r\n";
final ReadableByteChannel channel = new ReadableByteChannelMock(
@@ -242,16 +238,10 @@ public class TestChunkDecoder {
final ChunkDecoder decoder = new ChunkDecoder(channel, inbuf, metrics);
final ByteBuffer dst = ByteBuffer.allocate(1024);
-
- try {
- decoder.read(dst);
- Assert.fail("MalformedChunkCodingException should have been thrown");
- } catch (final MalformedChunkCodingException ex) {
- // expected
- }
+ decoder.read(dst);
}
- @Test
+ @Test(expected=TruncatedChunkException.class)
public void testMalformedChunkTruncatedChunk() throws Exception {
final String s = "3\r\n12";
final ReadableByteChannel channel = new ReadableByteChannelMock(
@@ -263,12 +253,7 @@ public class TestChunkDecoder {
final ByteBuffer dst = ByteBuffer.allocate(1024);
Assert.assertEquals(2, decoder.read(dst));
- try {
- decoder.read(dst);
- Assert.fail("MalformedChunkCodingException should have been thrown");
- } catch (final MalformedChunkCodingException ex) {
- // expected
- }
+ decoder.read(dst);
}
@Test
@@ -294,7 +279,7 @@ public class TestChunkDecoder {
Assert.assertEquals("abcde fghij", footers[0].getValue());
}
- @Test
+ @Test(expected=IOException.class)
public void testMalformedFooters() throws Exception {
final String s = "10;key=\"value\"\r\n1234567890123456\r\n" +
"5\r\n12345\r\n5\r\n12345\r\n0\r\nFooter1 abcde\r\n\r\n";
@@ -306,19 +291,31 @@ public class TestChunkDecoder {
final ChunkDecoder decoder = new ChunkDecoder(channel, inbuf, metrics);
final ByteBuffer dst = ByteBuffer.allocate(1024);
+ decoder.read(dst);
+ }
- try {
+ @Test(expected=MalformedChunkCodingException.class)
+ public void testMissingLastCRLF() throws Exception {
+ final String s = "10\r\n1234567890123456\r\n" +
+ "5\r\n12345\r\n5\r\n12345";
+ final ReadableByteChannel channel = new ReadableByteChannelMock(
+ new String[] {s}, Consts.ASCII);
+
+ final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, Consts.ASCII);
+ final HttpTransportMetricsImpl metrics = new HttpTransportMetricsImpl();
+ final ChunkDecoder decoder = new ChunkDecoder(channel, inbuf, metrics);
+
+ final ByteBuffer dst = ByteBuffer.allocate(1024);
+
+ while (dst.hasRemaining() && !decoder.isCompleted()) {
decoder.read(dst);
- Assert.fail("MalformedChunkCodingException should have been thrown");
- } catch (final IOException ex) {
- // expected
}
}
- @Test
- public void testEndOfStreamConditionReadingLastChunk() throws Exception {
+ @Test(expected=ConnectionClosedException.class)
+ public void testMissingClosingChunk() throws Exception {
final String s = "10\r\n1234567890123456\r\n" +
- "5\r\n12345\r\n5\r\n12345";
+ "5\r\n12345\r\n5\r\n12345\r\n";
final ReadableByteChannel channel = new ReadableByteChannelMock(
new String[] {s}, Consts.ASCII);
@@ -328,17 +325,20 @@ public class TestChunkDecoder {
final ByteBuffer dst = ByteBuffer.allocate(1024);
- int bytesRead = 0;
- while (dst.hasRemaining() && !decoder.isCompleted()) {
- final int i = decoder.read(dst);
- if (i > 0) {
- bytesRead += i;
+ long bytesRead = 0;
+ try {
+ while (dst.hasRemaining() && !decoder.isCompleted()) {
+ final int i = decoder.read(dst);
+ if (i > 0) {
+ bytesRead += i;
+ }
}
+ } catch (final MalformedChunkCodingException ex) {
+ Assert.assertEquals(26L, bytesRead);
+ Assert.assertEquals("12345678901234561234512345", CodecTestUtils.convert(dst));
+ Assert.assertTrue(decoder.isCompleted());
+ throw ex;
}
-
- Assert.assertEquals(26, bytesRead);
- Assert.assertEquals("12345678901234561234512345", CodecTestUtils.convert(dst));
- Assert.assertTrue(decoder.isCompleted());
}
@Test
@@ -426,7 +426,7 @@ public class TestChunkDecoder {
}
}
- @Test
+ @Test(expected=IllegalArgumentException.class)
public void testInvalidInput() throws Exception {
final String s = "10;key=\"value\"\r\n1234567890123456\r\n" +
"5\r\n12345\r\n5\r\n12345\r\n0\r\nFooter1 abcde\r\n\r\n";
@@ -436,13 +436,7 @@ public class TestChunkDecoder {
final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, Consts.ASCII);
final HttpTransportMetricsImpl metrics = new HttpTransportMetricsImpl();
final ChunkDecoder decoder = new ChunkDecoder(channel, inbuf, metrics);
-
- try {
- decoder.read(null);
- Assert.fail("IllegalArgumentException should have been thrown");
- } catch (final IllegalArgumentException ex) {
- // expected
- }
+ decoder.read(null);
}
}
Modified: httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/impl/io/ChunkedInputStream.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/impl/io/ChunkedInputStream.java?rev=1565641&r1=1565640&r2=1565641&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/impl/io/ChunkedInputStream.java (original)
+++ httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/impl/io/ChunkedInputStream.java Fri Feb 7 13:28:02 2014
@@ -30,6 +30,7 @@ package org.apache.http.impl.io;
import java.io.IOException;
import java.io.InputStream;
+import org.apache.http.ConnectionClosedException;
import org.apache.http.Header;
import org.apache.http.HttpException;
import org.apache.http.MalformedChunkCodingException;
@@ -227,7 +228,8 @@ public class ChunkedInputStream extends
this.buffer.clear();
final int bytesRead1 = this.in.readLine(this.buffer);
if (bytesRead1 == -1) {
- return 0;
+ throw new MalformedChunkCodingException(
+ "CRLF expected at end of chunk");
}
if (!this.buffer.isEmpty()) {
throw new MalformedChunkCodingException(
@@ -239,7 +241,8 @@ public class ChunkedInputStream extends
this.buffer.clear();
final int bytesRead2 = this.in.readLine(this.buffer);
if (bytesRead2 == -1) {
- return 0;
+ throw new ConnectionClosedException("Premature end of chunk coded message body: " +
+ "closing chunk expected");
}
int separator = this.buffer.indexOf(';');
if (separator < 0) {
Modified: httpcomponents/httpcore/trunk/httpcore/src/test/java/org/apache/http/impl/io/TestChunkCoding.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore/src/test/java/org/apache/http/impl/io/TestChunkCoding.java?rev=1565641&r1=1565640&r2=1565641&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore/src/test/java/org/apache/http/impl/io/TestChunkCoding.java (original)
+++ httpcomponents/httpcore/trunk/httpcore/src/test/java/org/apache/http/impl/io/TestChunkCoding.java Fri Feb 7 13:28:02 2014
@@ -33,6 +33,7 @@ import java.io.InputStream;
import java.io.InterruptedIOException;
import java.io.OutputStream;
+import org.apache.http.ConnectionClosedException;
import org.apache.http.Consts;
import org.apache.http.Header;
import org.apache.http.MalformedChunkCodingException;
@@ -195,19 +196,29 @@ public class TestChunkCoding {
}
// Missing closing chunk
- @Test
+ @Test(expected=ConnectionClosedException.class)
public void testChunkedInputStreamNoClosingChunk() throws IOException {
final String s = "5\r\n01234\r\n";
final ChunkedInputStream in = new ChunkedInputStream(
new SessionInputBufferMock(s, Consts.ISO_8859_1));
final byte[] tmp = new byte[5];
Assert.assertEquals(5, in.read(tmp));
- Assert.assertEquals(-1, in.read());
- in.close();
-}
+ in.read();
+ }
+
+ // Truncated stream (missing closing CRLF)
+ @Test(expected=MalformedChunkCodingException.class)
+ public void testCorruptChunkedInputStreamTruncatedCRLF() throws IOException {
+ final String s = "5\r\n01234";
+ final ChunkedInputStream in = new ChunkedInputStream(
+ new SessionInputBufferMock(s, Consts.ISO_8859_1));
+ final byte[] tmp = new byte[5];
+ Assert.assertEquals(5, in.read(tmp));
+ in.read();
+ }
// Missing \r\n at the end of the first chunk
- @Test
+ @Test(expected=MalformedChunkCodingException.class)
public void testCorruptChunkedInputStreamMissingCRLF() throws IOException {
final String s = "5\r\n012345\r\n56789\r\n0\r\n";
final InputStream in = new ChunkedInputStream(
@@ -215,29 +226,18 @@ public class TestChunkCoding {
final byte[] buffer = new byte[300];
final ByteArrayOutputStream out = new ByteArrayOutputStream();
int len;
- try {
- while ((len = in.read(buffer)) > 0) {
- out.write(buffer, 0, len);
- }
- Assert.fail("MalformedChunkCodingException should have been thrown");
- } catch(final MalformedChunkCodingException e) {
- /* expected exception */
+ while ((len = in.read(buffer)) > 0) {
+ out.write(buffer, 0, len);
}
}
// Missing LF
- @Test
+ @Test(expected=MalformedChunkCodingException.class)
public void testCorruptChunkedInputStreamMissingLF() throws IOException {
final String s = "5\r01234\r\n5\r\n56789\r\n0\r\n";
final InputStream in = new ChunkedInputStream(
new SessionInputBufferMock(s, Consts.ISO_8859_1));
- try {
- in.read();
- Assert.fail("MalformedChunkCodingException should have been thrown");
- } catch(final MalformedChunkCodingException e) {
- /* expected exception */
- }
- in.close();
+ in.read();
}
// Invalid chunk size