ChunkDecoder extends AbstractContentDecoder { @Override public int read(final ByteBuffer dst) throws IOException { Args.notNull(dst, "Byte buffer"); if (this.state == State.COMPLETED) { return -1; } int totalRead = 0; while (this.state != State.COMPLETED) { if (!this.buffer.hasData() || this.chunkSize == -1L) { final int bytesRead = fillBufferFromChannel(); if (bytesRead == -1) { this.endOfStream = true; } } switch (this.state) { case READ_CONTENT: if (this.chunkSize == -1L) { readChunkHead(); if (this.chunkSize == -1L) { return totalRead; } if (this.chunkSize == 0L) { this.chunkSize = -1L; this.state = State.READ_FOOTERS; break; } } final long maxLen = this.chunkSize - this.pos; final int len = this.buffer.read(dst, (int) Math.min(maxLen, Integer.MAX_VALUE)); if (len > 0) { this.pos += len; totalRead += len; } else { if (!this.buffer.hasData() && this.endOfStream) { this.state = State.COMPLETED; setCompleted(); throw new TruncatedChunkException( "Truncated chunk (expected size: %d; actual size: %d)", chunkSize, pos); } } if (this.pos == this.chunkSize) { this.chunkSize = -1L; this.pos = 0L; this.endOfChunk = true; break; } return totalRead; case READ_FOOTERS: if (this.lineBuf == null) { this.lineBuf = new CharArrayBuffer(32); } else { this.lineBuf.clear(); } if (!this.buffer.readLine(this.lineBuf, this.endOfStream)) { if (this.endOfStream) { this.state = State.COMPLETED; setCompleted(); } return totalRead; } if (this.lineBuf.length() > 0) { final int maxHeaderCount = this.http1Config.getMaxHeaderCount(); if (maxHeaderCount > 0 && trailerBufs.size() >= maxHeaderCount) { throw new MessageConstraintException("Maximum header count exceeded"); } parseHeader(); } else { this.state = State.COMPLETED; setCompleted(); processFooters(); } break; } } return totalRead; } ChunkDecoder(
            final ReadableByteChannel channel,
            final SessionInputBuffer buffer,
            final Http1Config http1Config,
            final BasicHttpTransportMetrics metrics);  ChunkDecoder(
            final ReadableByteChannel channel,
            final SessionInputBuffer buffer,
            final BasicHttpTransportMetrics metrics); @Override int read(final ByteBuffer dst); @Override List<? extends Header> getTrailers(); @Override String toString();  }
@Test public void testDecodingWithSmallBuffer() throws Exception { final String s1 = "5\r\n01234\r\n5\r\n5678"; final String s2 = "9\r\n6\r\nabcdef\r\n0\r\n\r\n"; final ReadableByteChannel channel = new ReadableByteChannelMock( new String[] {s1, s2}, StandardCharsets.US_ASCII); final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, 0, StandardCharsets.US_ASCII); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final ChunkDecoder decoder = new ChunkDecoder(channel, inbuf, metrics); final ByteBuffer dst = ByteBuffer.allocate(1024); final ByteBuffer tmp = ByteBuffer.allocate(4); int bytesRead = 0; while (dst.hasRemaining() && !decoder.isCompleted()) { final int i = decoder.read(tmp); if (i > 0) { bytesRead += i; } tmp.flip(); dst.put(tmp); tmp.compact(); } Assert.assertEquals(16, bytesRead); Assert.assertEquals("0123456789abcdef", CodecTestUtils.convert(dst)); Assert.assertTrue(decoder.isCompleted()); dst.clear(); bytesRead = decoder.read(dst); Assert.assertEquals(-1, bytesRead); Assert.assertTrue(decoder.isCompleted()); }
@Test public void testMalformedChunk() throws Exception { final String s = "5\r\n01234----------------------------------------------------------" + "-----------------------------------------------------------------------------" + "-----------------------------------------------------------------------------"; final ReadableByteChannel channel = new ReadableByteChannelMock( new String[] {s}, StandardCharsets.US_ASCII); final SessionInputBuffer inbuf = new SessionInputBufferImpl(32, 32, 0, StandardCharsets.US_ASCII); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); 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) { } }
@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( new String[] {s}, StandardCharsets.US_ASCII); final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, 0, StandardCharsets.US_ASCII); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final ChunkDecoder decoder = new ChunkDecoder(channel, inbuf, metrics); final ByteBuffer dst = ByteBuffer.allocate(1024); decoder.read(dst); }
@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( new String[] {s}, StandardCharsets.US_ASCII); final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, 0, StandardCharsets.US_ASCII); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final ChunkDecoder decoder = new ChunkDecoder(channel, inbuf, metrics); final ByteBuffer dst = ByteBuffer.allocate(1024); decoder.read(dst); }
@Test(expected=TruncatedChunkException.class) public void testMalformedChunkTruncatedChunk() throws Exception { final String s = "3\r\n12"; final ReadableByteChannel channel = new ReadableByteChannelMock( new String[] {s}, StandardCharsets.US_ASCII); final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, 0, StandardCharsets.US_ASCII); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final ChunkDecoder decoder = new ChunkDecoder(channel, inbuf, metrics); final ByteBuffer dst = ByteBuffer.allocate(1024); Assert.assertEquals(2, decoder.read(dst)); decoder.read(dst); }
@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"; final ReadableByteChannel channel = new ReadableByteChannelMock( new String[] {s}, StandardCharsets.US_ASCII); final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, 0, StandardCharsets.US_ASCII); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final ChunkDecoder decoder = new ChunkDecoder(channel, inbuf, metrics); final ByteBuffer dst = ByteBuffer.allocate(1024); decoder.read(dst); }
@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}, StandardCharsets.US_ASCII); final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, 0, StandardCharsets.US_ASCII); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final ChunkDecoder decoder = new ChunkDecoder(channel, inbuf, metrics); final ByteBuffer dst = ByteBuffer.allocate(1024); while (dst.hasRemaining() && !decoder.isCompleted()) { decoder.read(dst); } }
@Test(expected=ConnectionClosedException.class) public void testMissingClosingChunk() throws Exception { final String s = "10\r\n1234567890123456\r\n" + "5\r\n12345\r\n5\r\n12345\r\n"; final ReadableByteChannel channel = new ReadableByteChannelMock( new String[] {s}, StandardCharsets.US_ASCII); final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, 0, StandardCharsets.US_ASCII); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final ChunkDecoder decoder = new ChunkDecoder(channel, inbuf, metrics); final ByteBuffer dst = ByteBuffer.allocate(1024); 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; } }
@Test public void testReadingWitSmallBuffer() throws Exception { final String s = "10\r\n1234567890123456\r\n" + "40\r\n12345678901234561234567890123456" + "12345678901234561234567890123456\r\n0\r\n"; final ReadableByteChannel channel = new ReadableByteChannelMock( new String[] {s}, StandardCharsets.US_ASCII); final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, 0, StandardCharsets.US_ASCII); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final ChunkDecoder decoder = new ChunkDecoder(channel, inbuf, metrics); final ByteBuffer dst = ByteBuffer.allocate(1024); final ByteBuffer tmp = ByteBuffer.allocate(10); int bytesRead = 0; while (dst.hasRemaining() && !decoder.isCompleted()) { final int i = decoder.read(tmp); if (i > 0) { bytesRead += i; tmp.flip(); dst.put(tmp); tmp.compact(); } } Assert.assertEquals(80, bytesRead); Assert.assertEquals("12345678901234561234567890123456" + "12345678901234561234567890123456" + "1234567890123456", CodecTestUtils.convert(dst)); Assert.assertTrue(decoder.isCompleted()); }
@Test public void testEndOfStreamConditionReadingFooters() throws Exception { final String s = "10\r\n1234567890123456\r\n" + "5\r\n12345\r\n5\r\n12345\r\n0\r\n"; final ReadableByteChannel channel = new ReadableByteChannelMock( new String[] {s}, StandardCharsets.US_ASCII); final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, 0, StandardCharsets.US_ASCII); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final ChunkDecoder decoder = new ChunkDecoder(channel, inbuf, metrics); 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; } } Assert.assertEquals(26, bytesRead); Assert.assertEquals("12345678901234561234512345", CodecTestUtils.convert(dst)); Assert.assertTrue(decoder.isCompleted()); }
@Test public void testTooLongChunkHeader() throws Exception { final String s = "5; and some very looooong comment\r\n12345\r\n0\r\n"; final ReadableByteChannel channel1 = new ReadableByteChannelMock( new String[] {s}, StandardCharsets.US_ASCII); final BasicHttpTransportMetrics metrics1 = new BasicHttpTransportMetrics(); final SessionInputBuffer inbuf1 = new SessionInputBufferImpl(1024, 256); final ChunkDecoder decoder1 = new ChunkDecoder(channel1, inbuf1, metrics1); final ByteBuffer dst = ByteBuffer.allocate(1024); while (dst.hasRemaining() && !decoder1.isCompleted()) { decoder1.read(dst); } Assert.assertEquals("12345", CodecTestUtils.convert(dst)); Assert.assertTrue(decoder1.isCompleted()); final ReadableByteChannel channel2 = new ReadableByteChannelMock( new String[] {s}, StandardCharsets.US_ASCII); final SessionInputBuffer inbuf2 = new SessionInputBufferImpl(1024, 256, 10); final BasicHttpTransportMetrics metrics2 = new BasicHttpTransportMetrics(); final ChunkDecoder decoder2 = new ChunkDecoder(channel2, inbuf2, metrics2); dst.clear(); try { decoder2.read(dst); Assert.fail("MessageConstraintException expected"); } catch (final MessageConstraintException ex) { } }
@Test(expected=NullPointerException.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"; final ReadableByteChannel channel = new ReadableByteChannelMock( new String[] {s}, StandardCharsets.US_ASCII); final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, 0, StandardCharsets.US_ASCII); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final ChunkDecoder decoder = new ChunkDecoder(channel, inbuf, metrics); decoder.read(null); }
@Test public void testHugeChunk() throws Exception { final String s = "1234567890abcdef\r\n0123456789abcdef"; final ReadableByteChannel channel = new ReadableByteChannelMock(new String[] {s}, StandardCharsets.US_ASCII); final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, 0, StandardCharsets.US_ASCII); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final ChunkDecoder decoder = new ChunkDecoder(channel, inbuf, metrics); final ByteBuffer dst = ByteBuffer.allocate(4); int bytesRead = decoder.read(dst); Assert.assertEquals(4, bytesRead); Assert.assertEquals("0123", CodecTestUtils.convert(dst)); dst.clear(); bytesRead = decoder.read(dst); Assert.assertEquals(4, bytesRead); Assert.assertEquals("4567", CodecTestUtils.convert(dst)); }