blob: 06d431dc9025a3906b8dec63298426999a576874 [file] [log] [blame]
/*
* Copyright 2017 The gRPC Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.grpc.internal;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import com.google.common.io.ByteStreams;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.zip.CRC32;
import java.util.zip.DataFormatException;
import java.util.zip.GZIPOutputStream;
import java.util.zip.ZipException;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests for {@link GzipInflatingBuffer}. */
@RunWith(JUnit4.class)
public class GzipInflatingBufferTest {
private static final String UNCOMPRESSABLE_FILE = "/io/grpc/internal/uncompressable.bin";
private static final int GZIP_HEADER_MIN_SIZE = 10;
private static final int GZIP_TRAILER_SIZE = 8;
private static final int GZIP_HEADER_FLAG_INDEX = 3;
public static final int GZIP_MAGIC = 0x8b1f;
private static final int FTEXT = 1;
private static final int FHCRC = 2;
private static final int FEXTRA = 4;
private static final int FNAME = 8;
private static final int FCOMMENT = 16;
private static final int TRUNCATED_DATA_SIZE = 10;
private byte[] originalData;
private byte[] gzippedData;
private byte[] gzipHeader;
private byte[] deflatedBytes;
private byte[] gzipTrailer;
private byte[] truncatedData;
private byte[] gzippedTruncatedData;
private GzipInflatingBuffer gzipInflatingBuffer;
@Before
public void setUp() {
gzipInflatingBuffer = new GzipInflatingBuffer();
try {
originalData = ByteStreams.toByteArray(getClass().getResourceAsStream(UNCOMPRESSABLE_FILE));
truncatedData = Arrays.copyOf(originalData, TRUNCATED_DATA_SIZE);
ByteArrayOutputStream gzippedOutputStream = new ByteArrayOutputStream();
OutputStream gzippingOutputStream = new GZIPOutputStream(gzippedOutputStream);
gzippingOutputStream.write(originalData);
gzippingOutputStream.close();
gzippedData = gzippedOutputStream.toByteArray();
gzippedOutputStream.close();
gzipHeader = Arrays.copyOf(gzippedData, GZIP_HEADER_MIN_SIZE);
deflatedBytes =
Arrays.copyOfRange(
gzippedData, GZIP_HEADER_MIN_SIZE, gzippedData.length - GZIP_TRAILER_SIZE);
gzipTrailer =
Arrays.copyOfRange(
gzippedData, gzippedData.length - GZIP_TRAILER_SIZE, gzippedData.length);
ByteArrayOutputStream truncatedGzippedOutputStream = new ByteArrayOutputStream();
OutputStream smallerGzipCompressingStream =
new GZIPOutputStream(truncatedGzippedOutputStream);
smallerGzipCompressingStream.write(truncatedData);
smallerGzipCompressingStream.close();
gzippedTruncatedData = truncatedGzippedOutputStream.toByteArray();
truncatedGzippedOutputStream.close();
} catch (Exception e) {
throw new RuntimeException("Failed to set up compressed data", e);
}
}
@After
public void tearDown() {
gzipInflatingBuffer.close();
}
@Test
public void gzipInflateWorks() throws Exception {
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzippedData));
byte[] b = new byte[originalData.length];
assertEquals(originalData.length, gzipInflatingBuffer.inflateBytes(b, 0, originalData.length));
assertEquals(gzippedData.length, gzipInflatingBuffer.getAndResetBytesConsumed());
assertTrue("inflated data does not match", Arrays.equals(originalData, b));
}
@Test
public void splitGzipStreamWorks() throws Exception {
int initialBytes = 100;
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzippedData, 0, initialBytes));
byte[] b = new byte[originalData.length];
int n = gzipInflatingBuffer.inflateBytes(b, 0, originalData.length);
assertTrue("inflated bytes expected", n > 0);
assertTrue("gzipInflatingBuffer is not stalled", gzipInflatingBuffer.isStalled());
assertEquals(initialBytes, gzipInflatingBuffer.getAndResetBytesConsumed());
gzipInflatingBuffer.addGzippedBytes(
ReadableBuffers.wrap(gzippedData, initialBytes, gzippedData.length - initialBytes));
int bytesRemaining = originalData.length - n;
assertEquals(bytesRemaining, gzipInflatingBuffer.inflateBytes(b, n, bytesRemaining));
assertEquals(gzippedData.length - initialBytes, gzipInflatingBuffer.getAndResetBytesConsumed());
assertTrue("inflated data does not match", Arrays.equals(originalData, b));
}
@Test
public void inflateBytesObeysOffsetAndLength() throws Exception {
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzippedData));
int offset = 10;
int length = 100;
byte[] b = new byte[offset + length + offset];
assertEquals(length, gzipInflatingBuffer.inflateBytes(b, offset, length));
assertTrue(
"bytes written before offset",
Arrays.equals(new byte[offset], Arrays.copyOfRange(b, 0, offset)));
assertTrue(
"inflated data does not match",
Arrays.equals(
Arrays.copyOfRange(originalData, 0, length),
Arrays.copyOfRange(b, offset, offset + length)));
assertTrue(
"bytes written beyond length",
Arrays.equals(
new byte[offset], Arrays.copyOfRange(b, offset + length, offset + length + offset)));
}
@Test
public void concatenatedStreamsWorks() throws Exception {
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzippedData));
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzippedTruncatedData));
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzippedData));
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzippedTruncatedData));
byte[] b = new byte[originalData.length];
assertEquals(originalData.length, gzipInflatingBuffer.inflateBytes(b, 0, originalData.length));
assertEquals(gzippedData.length, gzipInflatingBuffer.getAndResetBytesConsumed());
assertTrue("inflated data does not match", Arrays.equals(originalData, b));
assertEquals(
truncatedData.length, gzipInflatingBuffer.inflateBytes(b, 0, truncatedData.length));
assertEquals(gzippedTruncatedData.length, gzipInflatingBuffer.getAndResetBytesConsumed());
assertTrue("inflated data does not match", Arrays.equals(originalData, b));
assertEquals(originalData.length, gzipInflatingBuffer.inflateBytes(b, 0, originalData.length));
assertEquals(gzippedData.length, gzipInflatingBuffer.getAndResetBytesConsumed());
assertTrue("inflated data does not match", Arrays.equals(originalData, b));
assertEquals(
truncatedData.length, gzipInflatingBuffer.inflateBytes(b, 0, truncatedData.length));
assertEquals(gzippedTruncatedData.length, gzipInflatingBuffer.getAndResetBytesConsumed());
assertTrue("inflated data does not match", Arrays.equals(originalData, b));
}
@Test
public void requestingTooManyBytesStillReturnsEndOfBlock() throws Exception {
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzippedData));
int len = 2 * originalData.length;
byte[] b = new byte[len];
assertEquals(originalData.length, gzipInflatingBuffer.inflateBytes(b, 0, len));
assertEquals(gzippedData.length, gzipInflatingBuffer.getAndResetBytesConsumed());
assertTrue(gzipInflatingBuffer.isStalled());
assertTrue(
"inflated data does not match",
Arrays.equals(originalData, Arrays.copyOf(b, originalData.length)));
}
@Test
public void closeStopsDecompression() throws Exception {
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzippedData));
byte[] b = new byte[1];
gzipInflatingBuffer.inflateBytes(b, 0, 1);
gzipInflatingBuffer.close();
try {
gzipInflatingBuffer.inflateBytes(b, 0, 1);
fail("Expected IllegalStateException");
} catch (IllegalStateException expectedException) {
assertEquals("GzipInflatingBuffer is closed", expectedException.getMessage());
}
}
@Test
public void isStalledReturnsTrueAtEndOfStream() throws Exception {
int bytesToWithhold = 10;
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzippedData));
byte[] b = new byte[originalData.length];
gzipInflatingBuffer.inflateBytes(b, 0, originalData.length - bytesToWithhold);
assertFalse("gzipInflatingBuffer is stalled", gzipInflatingBuffer.isStalled());
gzipInflatingBuffer.inflateBytes(b, originalData.length - bytesToWithhold, bytesToWithhold);
assertTrue("inflated data does not match", Arrays.equals(originalData, b));
assertTrue("gzipInflatingBuffer is not stalled", gzipInflatingBuffer.isStalled());
}
@Test
public void isStalledReturnsFalseBetweenStreams() throws Exception {
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzippedData));
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzippedData));
byte[] b = new byte[originalData.length];
assertEquals(originalData.length, gzipInflatingBuffer.inflateBytes(b, 0, originalData.length));
assertTrue("inflated data does not match", Arrays.equals(originalData, b));
assertFalse("gzipInflatingBuffer is stalled", gzipInflatingBuffer.isStalled());
assertEquals(originalData.length, gzipInflatingBuffer.inflateBytes(b, 0, originalData.length));
assertTrue("inflated data does not match", Arrays.equals(originalData, b));
assertTrue("gzipInflatingBuffer is not stalled", gzipInflatingBuffer.isStalled());
}
@Test
public void isStalledReturnsFalseBetweenSmallStreams() throws Exception {
// Use small streams to make sure that they all fit in the inflater buffer
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzippedTruncatedData));
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzippedTruncatedData));
byte[] b = new byte[truncatedData.length];
assertEquals(
truncatedData.length, gzipInflatingBuffer.inflateBytes(b, 0, truncatedData.length));
assertTrue("inflated data does not match", Arrays.equals(truncatedData, b));
assertFalse("gzipInflatingBuffer is stalled", gzipInflatingBuffer.isStalled());
assertEquals(
truncatedData.length, gzipInflatingBuffer.inflateBytes(b, 0, truncatedData.length));
assertTrue("inflated data does not match", Arrays.equals(truncatedData, b));
assertTrue("gzipInflatingBuffer is not stalled", gzipInflatingBuffer.isStalled());
}
@Test
public void isStalledReturnsTrueWithPartialNextHeaderAvailable() throws Exception {
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzippedTruncatedData));
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(new byte[1]));
byte[] b = new byte[truncatedData.length];
assertEquals(
truncatedData.length, gzipInflatingBuffer.inflateBytes(b, 0, truncatedData.length));
assertTrue("inflated data does not match", Arrays.equals(truncatedData, b));
assertTrue("gzipInflatingBuffer is not stalled", gzipInflatingBuffer.isStalled());
assertTrue("partial data expected", gzipInflatingBuffer.hasPartialData());
}
@Test
public void isStalledWorksWithAllHeaderFlags() throws Exception {
gzipHeader[GZIP_HEADER_FLAG_INDEX] =
(byte) (gzipHeader[GZIP_HEADER_FLAG_INDEX] | FTEXT | FHCRC | FEXTRA | FNAME | FCOMMENT);
int len = 1025;
byte[] fExtraLen = {(byte) len, (byte) (len >> 8)};
byte[] fExtra = new byte[len];
byte[] zeroTerminatedBytes = new byte[len];
for (int i = 0; i < len - 1; i++) {
zeroTerminatedBytes[i] = 1;
}
ByteArrayOutputStream newHeader = new ByteArrayOutputStream();
newHeader.write(gzipHeader);
newHeader.write(fExtraLen);
newHeader.write(fExtra);
newHeader.write(zeroTerminatedBytes); // FNAME
newHeader.write(zeroTerminatedBytes); // FCOMMENT
byte[] headerCrc16 = getHeaderCrc16Bytes(newHeader.toByteArray());
assertTrue("gzipInflatingBuffer is not stalled", gzipInflatingBuffer.isStalled());
addInTwoChunksAndVerifyIsStalled(gzipHeader);
addInTwoChunksAndVerifyIsStalled(fExtraLen);
addInTwoChunksAndVerifyIsStalled(fExtra);
addInTwoChunksAndVerifyIsStalled(zeroTerminatedBytes);
addInTwoChunksAndVerifyIsStalled(zeroTerminatedBytes);
addInTwoChunksAndVerifyIsStalled(headerCrc16);
byte[] b = new byte[originalData.length];
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(deflatedBytes));
assertEquals(originalData.length, gzipInflatingBuffer.inflateBytes(b, 0, originalData.length));
addInTwoChunksAndVerifyIsStalled(gzipTrailer);
}
@Test
public void hasPartialData() throws Exception {
assertFalse("no partial data expected", gzipInflatingBuffer.hasPartialData());
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzippedData));
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(new byte[1]));
byte[] b = new byte[originalData.length];
assertEquals(originalData.length, gzipInflatingBuffer.inflateBytes(b, 0, originalData.length));
assertTrue("inflated data does not match", Arrays.equals(originalData, b));
assertTrue("partial data expected", gzipInflatingBuffer.hasPartialData());
}
@Test
public void hasPartialDataWithoutGzipTrailer() throws Exception {
assertFalse("no partial data expected", gzipInflatingBuffer.hasPartialData());
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzipHeader));
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(deflatedBytes));
byte[] b = new byte[originalData.length];
assertEquals(originalData.length, gzipInflatingBuffer.inflateBytes(b, 0, originalData.length));
assertTrue("inflated data does not match", Arrays.equals(originalData, b));
assertTrue("partial data expected", gzipInflatingBuffer.hasPartialData());
}
@Test
public void inflatingCompleteGzipStreamConsumesTrailer() throws Exception {
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzippedData));
byte[] b = new byte[originalData.length];
assertEquals(originalData.length, gzipInflatingBuffer.inflateBytes(b, 0, originalData.length));
assertTrue("inflated data does not match", Arrays.equals(originalData, b));
assertFalse("no partial data expected", gzipInflatingBuffer.hasPartialData());
}
@Test
public void bytesConsumedForPartiallyInflatedBlock() throws Exception {
int bytesToWithhold = 1;
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzippedTruncatedData));
byte[] b = new byte[truncatedData.length];
assertEquals(
truncatedData.length - bytesToWithhold,
gzipInflatingBuffer.inflateBytes(b, 0, truncatedData.length - bytesToWithhold));
assertEquals(
gzippedTruncatedData.length - bytesToWithhold - GZIP_TRAILER_SIZE,
gzipInflatingBuffer.getAndResetBytesConsumed());
assertEquals(
bytesToWithhold,
gzipInflatingBuffer.inflateBytes(
b, truncatedData.length - bytesToWithhold, bytesToWithhold));
assertEquals(
bytesToWithhold + GZIP_TRAILER_SIZE, gzipInflatingBuffer.getAndResetBytesConsumed());
assertTrue("inflated data does not match", Arrays.equals(truncatedData, b));
}
@Test
public void getAndResetCompressedBytesConsumedReportsHeaderFlagBytes() throws Exception {
gzipHeader[GZIP_HEADER_FLAG_INDEX] =
(byte) (gzipHeader[GZIP_HEADER_FLAG_INDEX] | FTEXT | FHCRC | FEXTRA | FNAME | FCOMMENT);
int len = 1025;
byte[] fExtraLen = {(byte) len, (byte) (len >> 8)};
byte[] fExtra = new byte[len];
byte[] zeroTerminatedBytes = new byte[len];
for (int i = 0; i < len - 1; i++) {
zeroTerminatedBytes[i] = 1;
}
ByteArrayOutputStream newHeader = new ByteArrayOutputStream();
newHeader.write(gzipHeader);
newHeader.write(fExtraLen);
newHeader.write(fExtra);
newHeader.write(zeroTerminatedBytes); // FNAME
newHeader.write(zeroTerminatedBytes); // FCOMMENT
byte[] headerCrc16 = getHeaderCrc16Bytes(newHeader.toByteArray());
byte[] b = new byte[originalData.length];
assertEquals(0, gzipInflatingBuffer.inflateBytes(b, 0, 1));
assertEquals(0, gzipInflatingBuffer.getAndResetBytesConsumed());
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzipHeader));
assertEquals(0, gzipInflatingBuffer.inflateBytes(b, 0, 1));
assertEquals(gzipHeader.length, gzipInflatingBuffer.getAndResetBytesConsumed());
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(fExtraLen));
assertEquals(0, gzipInflatingBuffer.inflateBytes(b, 0, 1));
assertEquals(fExtraLen.length, gzipInflatingBuffer.getAndResetBytesConsumed());
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(fExtra));
assertEquals(0, gzipInflatingBuffer.inflateBytes(b, 0, 1));
assertEquals(fExtra.length, gzipInflatingBuffer.getAndResetBytesConsumed());
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(zeroTerminatedBytes));
assertEquals(0, gzipInflatingBuffer.inflateBytes(b, 0, 1));
assertEquals(zeroTerminatedBytes.length, gzipInflatingBuffer.getAndResetBytesConsumed());
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(zeroTerminatedBytes));
assertEquals(0, gzipInflatingBuffer.inflateBytes(b, 0, 1));
assertEquals(zeroTerminatedBytes.length, gzipInflatingBuffer.getAndResetBytesConsumed());
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(headerCrc16));
assertEquals(0, gzipInflatingBuffer.inflateBytes(b, 0, 1));
assertEquals(headerCrc16.length, gzipInflatingBuffer.getAndResetBytesConsumed());
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(deflatedBytes));
assertEquals(originalData.length, gzipInflatingBuffer.inflateBytes(b, 0, originalData.length));
assertEquals(deflatedBytes.length, gzipInflatingBuffer.getAndResetBytesConsumed());
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzipTrailer));
assertEquals(0, gzipInflatingBuffer.inflateBytes(b, 0, 1));
assertEquals(gzipTrailer.length, gzipInflatingBuffer.getAndResetBytesConsumed());
}
@Test
public void getAndResetDeflatedBytesConsumedExcludesGzipMetadata() throws Exception {
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzippedData));
byte[] b = new byte[originalData.length];
assertEquals(originalData.length, gzipInflatingBuffer.inflateBytes(b, 0, originalData.length));
assertEquals(
gzippedData.length - GZIP_HEADER_MIN_SIZE - GZIP_TRAILER_SIZE,
gzipInflatingBuffer.getAndResetDeflatedBytesConsumed());
}
@Test
public void wrongHeaderMagicShouldFail() throws Exception {
gzipHeader[1] = (byte) ~(GZIP_MAGIC >> 8);
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzipHeader));
try {
byte[] b = new byte[1];
gzipInflatingBuffer.inflateBytes(b, 0, 1);
fail("Expected ZipException");
} catch (ZipException expectedException) {
assertEquals("Not in GZIP format", expectedException.getMessage());
}
}
@Test
public void wrongHeaderCompressionMethodShouldFail() throws Exception {
gzipHeader[2] = 7; // Should be 8
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzipHeader));
try {
byte[] b = new byte[1];
gzipInflatingBuffer.inflateBytes(b, 0, 1);
fail("Expected ZipException");
} catch (ZipException expectedException) {
assertEquals("Unsupported compression method", expectedException.getMessage());
}
}
@Test
public void allHeaderFlagsWork() throws Exception {
gzipHeader[GZIP_HEADER_FLAG_INDEX] =
(byte) (gzipHeader[GZIP_HEADER_FLAG_INDEX] | FTEXT | FHCRC | FEXTRA | FNAME | FCOMMENT);
int len = 1025;
byte[] fExtraLen = {(byte) len, (byte) (len >> 8)};
byte[] fExtra = new byte[len];
byte[] zeroTerminatedBytes = new byte[len];
for (int i = 0; i < len - 1; i++) {
zeroTerminatedBytes[i] = 1;
}
ByteArrayOutputStream newHeader = new ByteArrayOutputStream();
newHeader.write(gzipHeader);
newHeader.write(fExtraLen);
newHeader.write(fExtra);
newHeader.write(zeroTerminatedBytes); // FNAME
newHeader.write(zeroTerminatedBytes); // FCOMMENT
byte[] headerCrc16 = getHeaderCrc16Bytes(newHeader.toByteArray());
newHeader.write(headerCrc16);
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(newHeader.toByteArray()));
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(deflatedBytes));
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzipTrailer));
byte[] b = new byte[originalData.length];
gzipInflatingBuffer.inflateBytes(b, 0, originalData.length);
assertTrue("inflated data does not match", Arrays.equals(originalData, b));
}
@Test
public void headerFTextFlagIsIgnored() throws Exception {
gzipHeader[GZIP_HEADER_FLAG_INDEX] = (byte) (gzipHeader[GZIP_HEADER_FLAG_INDEX] | FTEXT);
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzipHeader));
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(deflatedBytes));
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzipTrailer));
byte[] b = new byte[originalData.length];
assertEquals(originalData.length, gzipInflatingBuffer.inflateBytes(b, 0, originalData.length));
assertEquals(gzippedData.length, gzipInflatingBuffer.getAndResetBytesConsumed());
assertTrue("inflated data does not match", Arrays.equals(originalData, b));
}
@Test
public void headerFhcrcFlagWorks() throws Exception {
gzipHeader[GZIP_HEADER_FLAG_INDEX] = (byte) (gzipHeader[GZIP_HEADER_FLAG_INDEX] | FHCRC);
byte[] headerCrc16 = getHeaderCrc16Bytes(gzipHeader);
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzipHeader));
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(headerCrc16));
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(deflatedBytes));
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzipTrailer));
byte[] b = new byte[originalData.length];
assertEquals(originalData.length, gzipInflatingBuffer.inflateBytes(b, 0, originalData.length));
assertEquals(
gzippedData.length + headerCrc16.length, gzipInflatingBuffer.getAndResetBytesConsumed());
assertTrue("inflated data does not match", Arrays.equals(originalData, b));
}
@Test
public void headerInvalidFhcrcFlagFails() throws Exception {
gzipHeader[GZIP_HEADER_FLAG_INDEX] = (byte) (gzipHeader[GZIP_HEADER_FLAG_INDEX] | FHCRC);
byte[] headerCrc16 = getHeaderCrc16Bytes(gzipHeader);
headerCrc16[0] = (byte) ~headerCrc16[0];
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzipHeader));
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(headerCrc16));
try {
byte[] b = new byte[1];
gzipInflatingBuffer.inflateBytes(b, 0, 1);
fail("Expected ZipException");
} catch (ZipException expectedException) {
assertEquals("Corrupt GZIP header", expectedException.getMessage());
}
}
@Test
public void headerFExtraFlagWorks() throws Exception {
gzipHeader[GZIP_HEADER_FLAG_INDEX] = (byte) (gzipHeader[GZIP_HEADER_FLAG_INDEX] | FEXTRA);
int len = 1025;
byte[] fExtraLen = {(byte) len, (byte) (len >> 8)};
byte[] fExtra = new byte[len];
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzipHeader));
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(fExtraLen));
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(fExtra));
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(deflatedBytes));
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzipTrailer));
byte[] b = new byte[originalData.length];
assertEquals(originalData.length, gzipInflatingBuffer.inflateBytes(b, 0, originalData.length));
assertEquals(
gzippedData.length + fExtraLen.length + fExtra.length,
gzipInflatingBuffer.getAndResetBytesConsumed());
assertTrue("inflated data does not match", Arrays.equals(originalData, b));
}
@Test
public void headerFExtraFlagWithZeroLenWorks() throws Exception {
gzipHeader[GZIP_HEADER_FLAG_INDEX] = (byte) (gzipHeader[GZIP_HEADER_FLAG_INDEX] | FEXTRA);
byte[] fExtraLen = new byte[2];
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzipHeader));
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(fExtraLen));
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(deflatedBytes));
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzipTrailer));
byte[] b = new byte[originalData.length];
assertEquals(originalData.length, gzipInflatingBuffer.inflateBytes(b, 0, originalData.length));
assertEquals(
gzippedData.length + fExtraLen.length, gzipInflatingBuffer.getAndResetBytesConsumed());
assertTrue("inflated data does not match", Arrays.equals(originalData, b));
}
@Test
public void headerFExtraFlagWithMissingExtraLenFails() throws Exception {
gzipHeader[GZIP_HEADER_FLAG_INDEX] = (byte) (gzipHeader[GZIP_HEADER_FLAG_INDEX] | FEXTRA);
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzipHeader));
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(deflatedBytes));
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzipTrailer));
try {
byte[] b = new byte[originalData.length];
gzipInflatingBuffer.inflateBytes(b, 0, originalData.length);
fail("Expected DataFormatException");
} catch (DataFormatException expectedException) {
assertTrue(
"wrong exception message",
expectedException.getMessage().startsWith("Inflater data format exception:"));
}
}
@Test
public void headerFExtraFlagWithMissingExtraBytesFails() throws Exception {
gzipHeader[GZIP_HEADER_FLAG_INDEX] = (byte) (gzipHeader[GZIP_HEADER_FLAG_INDEX] | FEXTRA);
int len = 5;
byte[] fExtraLen = {(byte) len, (byte) (len >> 8)};
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzipHeader));
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(fExtraLen));
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(deflatedBytes));
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzipTrailer));
try {
byte[] b = new byte[originalData.length];
gzipInflatingBuffer.inflateBytes(b, 0, originalData.length);
fail("Expected DataFormatException");
} catch (DataFormatException expectedException) {
assertTrue(
"wrong exception message",
expectedException.getMessage().startsWith("Inflater data format exception:"));
}
}
@Test
public void headerFNameFlagWorks() throws Exception {
gzipHeader[GZIP_HEADER_FLAG_INDEX] = (byte) (gzipHeader[GZIP_HEADER_FLAG_INDEX] | FNAME);
int len = 1025;
byte[] zeroTerminatedBytes = new byte[len];
for (int i = 0; i < len - 1; i++) {
zeroTerminatedBytes[i] = 1;
}
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzipHeader));
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(zeroTerminatedBytes));
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(deflatedBytes));
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzipTrailer));
byte[] b = new byte[originalData.length];
assertEquals(originalData.length, gzipInflatingBuffer.inflateBytes(b, 0, originalData.length));
assertEquals(gzippedData.length + len, gzipInflatingBuffer.getAndResetBytesConsumed());
assertTrue("inflated data does not match", Arrays.equals(originalData, b));
}
@Test
public void headerFNameFlagWithMissingBytesFail() throws Exception {
gzipHeader[GZIP_HEADER_FLAG_INDEX] = (byte) (gzipHeader[GZIP_HEADER_FLAG_INDEX] | FNAME);
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzipHeader));
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(deflatedBytes));
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzipTrailer));
try {
byte[] b = new byte[originalData.length];
gzipInflatingBuffer.inflateBytes(b, 0, originalData.length);
fail("Expected DataFormatException");
} catch (DataFormatException expectedException) {
assertTrue(
"wrong exception message",
expectedException.getMessage().startsWith("Inflater data format exception:"));
}
}
@Test
public void headerFCommentFlagWorks() throws Exception {
gzipHeader[GZIP_HEADER_FLAG_INDEX] = (byte) (gzipHeader[GZIP_HEADER_FLAG_INDEX] | FCOMMENT);
int len = 1025;
byte[] zeroTerminatedBytes = new byte[len];
for (int i = 0; i < len - 1; i++) {
zeroTerminatedBytes[i] = 1;
}
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzipHeader));
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(zeroTerminatedBytes));
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(deflatedBytes));
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzipTrailer));
byte[] b = new byte[originalData.length];
assertEquals(originalData.length, gzipInflatingBuffer.inflateBytes(b, 0, originalData.length));
assertEquals(gzippedData.length + len, gzipInflatingBuffer.getAndResetBytesConsumed());
assertTrue("inflated data does not match", Arrays.equals(originalData, b));
}
@Test
public void headerFCommentFlagWithMissingBytesFail() throws Exception {
gzipHeader[GZIP_HEADER_FLAG_INDEX] = (byte) (gzipHeader[GZIP_HEADER_FLAG_INDEX] | FCOMMENT);
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzipHeader));
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(deflatedBytes));
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzipTrailer));
try {
byte[] b = new byte[originalData.length];
gzipInflatingBuffer.inflateBytes(b, 0, originalData.length);
fail("Expected DataFormatException");
} catch (DataFormatException expectedException) {
assertTrue(
"wrong exception message",
expectedException.getMessage().startsWith("Inflater data format exception:"));
}
}
@Test
public void wrongTrailerCrcShouldFail() throws Exception {
gzipTrailer[0] = (byte) ~gzipTrailer[0];
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzipHeader));
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(deflatedBytes));
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzipTrailer));
try {
byte[] b = new byte[originalData.length];
gzipInflatingBuffer.inflateBytes(b, 0, originalData.length);
fail("Expected ZipException");
} catch (ZipException expectedException) {
assertEquals("Corrupt GZIP trailer", expectedException.getMessage());
}
}
@Test
public void wrongTrailerISizeShouldFail() throws Exception {
gzipTrailer[GZIP_TRAILER_SIZE - 1] = (byte) ~gzipTrailer[GZIP_TRAILER_SIZE - 1];
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzipHeader));
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(deflatedBytes));
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzipTrailer));
try {
byte[] b = new byte[originalData.length];
gzipInflatingBuffer.inflateBytes(b, 0, originalData.length);
fail("Expected ZipException");
} catch (ZipException expectedException) {
assertEquals("Corrupt GZIP trailer", expectedException.getMessage());
}
}
@Test
public void invalidDeflateBlockShouldFail() throws Exception {
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzipHeader));
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(new byte[10]));
try {
byte[] b = new byte[originalData.length];
gzipInflatingBuffer.inflateBytes(b, 0, originalData.length);
fail("Expected DataFormatException");
} catch (DataFormatException expectedException) {
assertTrue(
"wrong exception message",
expectedException.getMessage().startsWith("Inflater data format exception:"));
}
}
private void addInTwoChunksAndVerifyIsStalled(byte[] input) throws Exception {
byte[] b = new byte[1];
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(input, 0, input.length - 1));
assertFalse("gzipInflatingBuffer is stalled", gzipInflatingBuffer.isStalled());
assertEquals(0, gzipInflatingBuffer.inflateBytes(b, 0, 1));
assertTrue("gzipInflatingBuffer is not stalled", gzipInflatingBuffer.isStalled());
gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(input, input.length - 1, 1));
assertFalse("gzipInflatingBuffer is stalled", gzipInflatingBuffer.isStalled());
assertEquals(0, gzipInflatingBuffer.inflateBytes(b, 0, 1));
assertTrue("gzipInflatingBuffer is not stalled", gzipInflatingBuffer.isStalled());
}
private byte[] getHeaderCrc16Bytes(byte[] headerBytes) {
CRC32 crc = new CRC32();
crc.update(headerBytes);
byte[] headerCrc16 = {(byte) crc.getValue(), (byte) (crc.getValue() >> 8)};
return headerCrc16;
}
}