1 /* 2 * Copyright 2017 The gRPC Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package io.grpc.internal; 18 19 import static org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertFalse; 21 import static org.junit.Assert.assertTrue; 22 import static org.junit.Assert.fail; 23 24 import com.google.common.io.ByteStreams; 25 import java.io.ByteArrayOutputStream; 26 import java.io.OutputStream; 27 import java.util.Arrays; 28 import java.util.zip.CRC32; 29 import java.util.zip.DataFormatException; 30 import java.util.zip.GZIPOutputStream; 31 import java.util.zip.ZipException; 32 import org.junit.After; 33 import org.junit.Before; 34 import org.junit.Test; 35 import org.junit.runner.RunWith; 36 import org.junit.runners.JUnit4; 37 38 /** Unit tests for {@link GzipInflatingBuffer}. */ 39 @RunWith(JUnit4.class) 40 public class GzipInflatingBufferTest { 41 private static final String UNCOMPRESSABLE_FILE = "/io/grpc/internal/uncompressable.bin"; 42 43 private static final int GZIP_HEADER_MIN_SIZE = 10; 44 private static final int GZIP_TRAILER_SIZE = 8; 45 private static final int GZIP_HEADER_FLAG_INDEX = 3; 46 47 public static final int GZIP_MAGIC = 0x8b1f; 48 49 private static final int FTEXT = 1; 50 private static final int FHCRC = 2; 51 private static final int FEXTRA = 4; 52 private static final int FNAME = 8; 53 private static final int FCOMMENT = 16; 54 55 private static final int TRUNCATED_DATA_SIZE = 10; 56 57 private byte[] originalData; 58 private byte[] gzippedData; 59 private byte[] gzipHeader; 60 private byte[] deflatedBytes; 61 private byte[] gzipTrailer; 62 private byte[] truncatedData; 63 private byte[] gzippedTruncatedData; 64 65 private GzipInflatingBuffer gzipInflatingBuffer; 66 67 @Before setUp()68 public void setUp() { 69 gzipInflatingBuffer = new GzipInflatingBuffer(); 70 try { 71 originalData = ByteStreams.toByteArray(getClass().getResourceAsStream(UNCOMPRESSABLE_FILE)); 72 truncatedData = Arrays.copyOf(originalData, TRUNCATED_DATA_SIZE); 73 74 ByteArrayOutputStream gzippedOutputStream = new ByteArrayOutputStream(); 75 OutputStream gzippingOutputStream = new GZIPOutputStream(gzippedOutputStream); 76 gzippingOutputStream.write(originalData); 77 gzippingOutputStream.close(); 78 gzippedData = gzippedOutputStream.toByteArray(); 79 gzippedOutputStream.close(); 80 81 gzipHeader = Arrays.copyOf(gzippedData, GZIP_HEADER_MIN_SIZE); 82 deflatedBytes = 83 Arrays.copyOfRange( 84 gzippedData, GZIP_HEADER_MIN_SIZE, gzippedData.length - GZIP_TRAILER_SIZE); 85 gzipTrailer = 86 Arrays.copyOfRange( 87 gzippedData, gzippedData.length - GZIP_TRAILER_SIZE, gzippedData.length); 88 89 ByteArrayOutputStream truncatedGzippedOutputStream = new ByteArrayOutputStream(); 90 OutputStream smallerGzipCompressingStream = 91 new GZIPOutputStream(truncatedGzippedOutputStream); 92 smallerGzipCompressingStream.write(truncatedData); 93 smallerGzipCompressingStream.close(); 94 gzippedTruncatedData = truncatedGzippedOutputStream.toByteArray(); 95 truncatedGzippedOutputStream.close(); 96 } catch (Exception e) { 97 throw new RuntimeException("Failed to set up compressed data", e); 98 } 99 } 100 101 @After tearDown()102 public void tearDown() { 103 gzipInflatingBuffer.close(); 104 } 105 106 @Test gzipInflateWorks()107 public void gzipInflateWorks() throws Exception { 108 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzippedData)); 109 110 byte[] b = new byte[originalData.length]; 111 assertEquals(originalData.length, gzipInflatingBuffer.inflateBytes(b, 0, originalData.length)); 112 assertEquals(gzippedData.length, gzipInflatingBuffer.getAndResetBytesConsumed()); 113 assertTrue("inflated data does not match", Arrays.equals(originalData, b)); 114 } 115 116 @Test splitGzipStreamWorks()117 public void splitGzipStreamWorks() throws Exception { 118 int initialBytes = 100; 119 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzippedData, 0, initialBytes)); 120 121 byte[] b = new byte[originalData.length]; 122 int n = gzipInflatingBuffer.inflateBytes(b, 0, originalData.length); 123 assertTrue("inflated bytes expected", n > 0); 124 assertTrue("gzipInflatingBuffer is not stalled", gzipInflatingBuffer.isStalled()); 125 assertEquals(initialBytes, gzipInflatingBuffer.getAndResetBytesConsumed()); 126 127 gzipInflatingBuffer.addGzippedBytes( 128 ReadableBuffers.wrap(gzippedData, initialBytes, gzippedData.length - initialBytes)); 129 int bytesRemaining = originalData.length - n; 130 assertEquals(bytesRemaining, gzipInflatingBuffer.inflateBytes(b, n, bytesRemaining)); 131 assertEquals(gzippedData.length - initialBytes, gzipInflatingBuffer.getAndResetBytesConsumed()); 132 assertTrue("inflated data does not match", Arrays.equals(originalData, b)); 133 } 134 135 @Test inflateBytesObeysOffsetAndLength()136 public void inflateBytesObeysOffsetAndLength() throws Exception { 137 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzippedData)); 138 139 int offset = 10; 140 int length = 100; 141 byte[] b = new byte[offset + length + offset]; 142 assertEquals(length, gzipInflatingBuffer.inflateBytes(b, offset, length)); 143 assertTrue( 144 "bytes written before offset", 145 Arrays.equals(new byte[offset], Arrays.copyOfRange(b, 0, offset))); 146 assertTrue( 147 "inflated data does not match", 148 Arrays.equals( 149 Arrays.copyOfRange(originalData, 0, length), 150 Arrays.copyOfRange(b, offset, offset + length))); 151 assertTrue( 152 "bytes written beyond length", 153 Arrays.equals( 154 new byte[offset], Arrays.copyOfRange(b, offset + length, offset + length + offset))); 155 } 156 157 @Test concatenatedStreamsWorks()158 public void concatenatedStreamsWorks() throws Exception { 159 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzippedData)); 160 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzippedTruncatedData)); 161 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzippedData)); 162 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzippedTruncatedData)); 163 164 byte[] b = new byte[originalData.length]; 165 assertEquals(originalData.length, gzipInflatingBuffer.inflateBytes(b, 0, originalData.length)); 166 assertEquals(gzippedData.length, gzipInflatingBuffer.getAndResetBytesConsumed()); 167 assertTrue("inflated data does not match", Arrays.equals(originalData, b)); 168 169 assertEquals( 170 truncatedData.length, gzipInflatingBuffer.inflateBytes(b, 0, truncatedData.length)); 171 assertEquals(gzippedTruncatedData.length, gzipInflatingBuffer.getAndResetBytesConsumed()); 172 assertTrue("inflated data does not match", Arrays.equals(originalData, b)); 173 174 assertEquals(originalData.length, gzipInflatingBuffer.inflateBytes(b, 0, originalData.length)); 175 assertEquals(gzippedData.length, gzipInflatingBuffer.getAndResetBytesConsumed()); 176 assertTrue("inflated data does not match", Arrays.equals(originalData, b)); 177 178 assertEquals( 179 truncatedData.length, gzipInflatingBuffer.inflateBytes(b, 0, truncatedData.length)); 180 assertEquals(gzippedTruncatedData.length, gzipInflatingBuffer.getAndResetBytesConsumed()); 181 assertTrue("inflated data does not match", Arrays.equals(originalData, b)); 182 } 183 184 @Test requestingTooManyBytesStillReturnsEndOfBlock()185 public void requestingTooManyBytesStillReturnsEndOfBlock() throws Exception { 186 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzippedData)); 187 188 int len = 2 * originalData.length; 189 byte[] b = new byte[len]; 190 assertEquals(originalData.length, gzipInflatingBuffer.inflateBytes(b, 0, len)); 191 assertEquals(gzippedData.length, gzipInflatingBuffer.getAndResetBytesConsumed()); 192 assertTrue(gzipInflatingBuffer.isStalled()); 193 assertTrue( 194 "inflated data does not match", 195 Arrays.equals(originalData, Arrays.copyOf(b, originalData.length))); 196 } 197 198 @Test closeStopsDecompression()199 public void closeStopsDecompression() throws Exception { 200 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzippedData)); 201 202 byte[] b = new byte[1]; 203 gzipInflatingBuffer.inflateBytes(b, 0, 1); 204 gzipInflatingBuffer.close(); 205 try { 206 gzipInflatingBuffer.inflateBytes(b, 0, 1); 207 fail("Expected IllegalStateException"); 208 } catch (IllegalStateException expectedException) { 209 assertEquals("GzipInflatingBuffer is closed", expectedException.getMessage()); 210 } 211 } 212 213 @Test isStalledReturnsTrueAtEndOfStream()214 public void isStalledReturnsTrueAtEndOfStream() throws Exception { 215 int bytesToWithhold = 10; 216 217 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzippedData)); 218 219 byte[] b = new byte[originalData.length]; 220 gzipInflatingBuffer.inflateBytes(b, 0, originalData.length - bytesToWithhold); 221 assertFalse("gzipInflatingBuffer is stalled", gzipInflatingBuffer.isStalled()); 222 223 gzipInflatingBuffer.inflateBytes(b, originalData.length - bytesToWithhold, bytesToWithhold); 224 assertTrue("inflated data does not match", Arrays.equals(originalData, b)); 225 assertTrue("gzipInflatingBuffer is not stalled", gzipInflatingBuffer.isStalled()); 226 } 227 228 @Test isStalledReturnsFalseBetweenStreams()229 public void isStalledReturnsFalseBetweenStreams() throws Exception { 230 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzippedData)); 231 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzippedData)); 232 233 byte[] b = new byte[originalData.length]; 234 assertEquals(originalData.length, gzipInflatingBuffer.inflateBytes(b, 0, originalData.length)); 235 assertTrue("inflated data does not match", Arrays.equals(originalData, b)); 236 assertFalse("gzipInflatingBuffer is stalled", gzipInflatingBuffer.isStalled()); 237 238 assertEquals(originalData.length, gzipInflatingBuffer.inflateBytes(b, 0, originalData.length)); 239 assertTrue("inflated data does not match", Arrays.equals(originalData, b)); 240 assertTrue("gzipInflatingBuffer is not stalled", gzipInflatingBuffer.isStalled()); 241 } 242 243 @Test isStalledReturnsFalseBetweenSmallStreams()244 public void isStalledReturnsFalseBetweenSmallStreams() throws Exception { 245 // Use small streams to make sure that they all fit in the inflater buffer 246 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzippedTruncatedData)); 247 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzippedTruncatedData)); 248 249 byte[] b = new byte[truncatedData.length]; 250 assertEquals( 251 truncatedData.length, gzipInflatingBuffer.inflateBytes(b, 0, truncatedData.length)); 252 assertTrue("inflated data does not match", Arrays.equals(truncatedData, b)); 253 assertFalse("gzipInflatingBuffer is stalled", gzipInflatingBuffer.isStalled()); 254 255 assertEquals( 256 truncatedData.length, gzipInflatingBuffer.inflateBytes(b, 0, truncatedData.length)); 257 assertTrue("inflated data does not match", Arrays.equals(truncatedData, b)); 258 assertTrue("gzipInflatingBuffer is not stalled", gzipInflatingBuffer.isStalled()); 259 } 260 261 @Test isStalledReturnsTrueWithPartialNextHeaderAvailable()262 public void isStalledReturnsTrueWithPartialNextHeaderAvailable() throws Exception { 263 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzippedTruncatedData)); 264 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(new byte[1])); 265 266 byte[] b = new byte[truncatedData.length]; 267 assertEquals( 268 truncatedData.length, gzipInflatingBuffer.inflateBytes(b, 0, truncatedData.length)); 269 assertTrue("inflated data does not match", Arrays.equals(truncatedData, b)); 270 assertTrue("gzipInflatingBuffer is not stalled", gzipInflatingBuffer.isStalled()); 271 assertTrue("partial data expected", gzipInflatingBuffer.hasPartialData()); 272 } 273 274 @Test isStalledWorksWithAllHeaderFlags()275 public void isStalledWorksWithAllHeaderFlags() throws Exception { 276 gzipHeader[GZIP_HEADER_FLAG_INDEX] = 277 (byte) (gzipHeader[GZIP_HEADER_FLAG_INDEX] | FTEXT | FHCRC | FEXTRA | FNAME | FCOMMENT); 278 int len = 1025; 279 byte[] fExtraLen = {(byte) len, (byte) (len >> 8)}; 280 byte[] fExtra = new byte[len]; 281 byte[] zeroTerminatedBytes = new byte[len]; 282 for (int i = 0; i < len - 1; i++) { 283 zeroTerminatedBytes[i] = 1; 284 } 285 ByteArrayOutputStream newHeader = new ByteArrayOutputStream(); 286 newHeader.write(gzipHeader); 287 newHeader.write(fExtraLen); 288 newHeader.write(fExtra); 289 newHeader.write(zeroTerminatedBytes); // FNAME 290 newHeader.write(zeroTerminatedBytes); // FCOMMENT 291 byte[] headerCrc16 = getHeaderCrc16Bytes(newHeader.toByteArray()); 292 293 assertTrue("gzipInflatingBuffer is not stalled", gzipInflatingBuffer.isStalled()); 294 295 addInTwoChunksAndVerifyIsStalled(gzipHeader); 296 addInTwoChunksAndVerifyIsStalled(fExtraLen); 297 addInTwoChunksAndVerifyIsStalled(fExtra); 298 addInTwoChunksAndVerifyIsStalled(zeroTerminatedBytes); 299 addInTwoChunksAndVerifyIsStalled(zeroTerminatedBytes); 300 addInTwoChunksAndVerifyIsStalled(headerCrc16); 301 302 byte[] b = new byte[originalData.length]; 303 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(deflatedBytes)); 304 assertEquals(originalData.length, gzipInflatingBuffer.inflateBytes(b, 0, originalData.length)); 305 306 addInTwoChunksAndVerifyIsStalled(gzipTrailer); 307 } 308 309 @Test hasPartialData()310 public void hasPartialData() throws Exception { 311 assertFalse("no partial data expected", gzipInflatingBuffer.hasPartialData()); 312 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzippedData)); 313 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(new byte[1])); 314 315 byte[] b = new byte[originalData.length]; 316 assertEquals(originalData.length, gzipInflatingBuffer.inflateBytes(b, 0, originalData.length)); 317 assertTrue("inflated data does not match", Arrays.equals(originalData, b)); 318 assertTrue("partial data expected", gzipInflatingBuffer.hasPartialData()); 319 } 320 321 @Test hasPartialDataWithoutGzipTrailer()322 public void hasPartialDataWithoutGzipTrailer() throws Exception { 323 assertFalse("no partial data expected", gzipInflatingBuffer.hasPartialData()); 324 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzipHeader)); 325 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(deflatedBytes)); 326 327 byte[] b = new byte[originalData.length]; 328 assertEquals(originalData.length, gzipInflatingBuffer.inflateBytes(b, 0, originalData.length)); 329 assertTrue("inflated data does not match", Arrays.equals(originalData, b)); 330 assertTrue("partial data expected", gzipInflatingBuffer.hasPartialData()); 331 } 332 333 @Test inflatingCompleteGzipStreamConsumesTrailer()334 public void inflatingCompleteGzipStreamConsumesTrailer() throws Exception { 335 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzippedData)); 336 337 byte[] b = new byte[originalData.length]; 338 assertEquals(originalData.length, gzipInflatingBuffer.inflateBytes(b, 0, originalData.length)); 339 assertTrue("inflated data does not match", Arrays.equals(originalData, b)); 340 assertFalse("no partial data expected", gzipInflatingBuffer.hasPartialData()); 341 } 342 343 @Test bytesConsumedForPartiallyInflatedBlock()344 public void bytesConsumedForPartiallyInflatedBlock() throws Exception { 345 int bytesToWithhold = 1; 346 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzippedTruncatedData)); 347 348 byte[] b = new byte[truncatedData.length]; 349 assertEquals( 350 truncatedData.length - bytesToWithhold, 351 gzipInflatingBuffer.inflateBytes(b, 0, truncatedData.length - bytesToWithhold)); 352 assertEquals( 353 gzippedTruncatedData.length - bytesToWithhold - GZIP_TRAILER_SIZE, 354 gzipInflatingBuffer.getAndResetBytesConsumed()); 355 assertEquals( 356 bytesToWithhold, 357 gzipInflatingBuffer.inflateBytes( 358 b, truncatedData.length - bytesToWithhold, bytesToWithhold)); 359 assertEquals( 360 bytesToWithhold + GZIP_TRAILER_SIZE, gzipInflatingBuffer.getAndResetBytesConsumed()); 361 assertTrue("inflated data does not match", Arrays.equals(truncatedData, b)); 362 } 363 364 @Test getAndResetCompressedBytesConsumedReportsHeaderFlagBytes()365 public void getAndResetCompressedBytesConsumedReportsHeaderFlagBytes() throws Exception { 366 gzipHeader[GZIP_HEADER_FLAG_INDEX] = 367 (byte) (gzipHeader[GZIP_HEADER_FLAG_INDEX] | FTEXT | FHCRC | FEXTRA | FNAME | FCOMMENT); 368 int len = 1025; 369 byte[] fExtraLen = {(byte) len, (byte) (len >> 8)}; 370 byte[] fExtra = new byte[len]; 371 byte[] zeroTerminatedBytes = new byte[len]; 372 for (int i = 0; i < len - 1; i++) { 373 zeroTerminatedBytes[i] = 1; 374 } 375 ByteArrayOutputStream newHeader = new ByteArrayOutputStream(); 376 newHeader.write(gzipHeader); 377 newHeader.write(fExtraLen); 378 newHeader.write(fExtra); 379 newHeader.write(zeroTerminatedBytes); // FNAME 380 newHeader.write(zeroTerminatedBytes); // FCOMMENT 381 byte[] headerCrc16 = getHeaderCrc16Bytes(newHeader.toByteArray()); 382 383 byte[] b = new byte[originalData.length]; 384 assertEquals(0, gzipInflatingBuffer.inflateBytes(b, 0, 1)); 385 assertEquals(0, gzipInflatingBuffer.getAndResetBytesConsumed()); 386 387 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzipHeader)); 388 assertEquals(0, gzipInflatingBuffer.inflateBytes(b, 0, 1)); 389 assertEquals(gzipHeader.length, gzipInflatingBuffer.getAndResetBytesConsumed()); 390 391 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(fExtraLen)); 392 assertEquals(0, gzipInflatingBuffer.inflateBytes(b, 0, 1)); 393 assertEquals(fExtraLen.length, gzipInflatingBuffer.getAndResetBytesConsumed()); 394 395 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(fExtra)); 396 assertEquals(0, gzipInflatingBuffer.inflateBytes(b, 0, 1)); 397 assertEquals(fExtra.length, gzipInflatingBuffer.getAndResetBytesConsumed()); 398 399 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(zeroTerminatedBytes)); 400 assertEquals(0, gzipInflatingBuffer.inflateBytes(b, 0, 1)); 401 assertEquals(zeroTerminatedBytes.length, gzipInflatingBuffer.getAndResetBytesConsumed()); 402 403 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(zeroTerminatedBytes)); 404 assertEquals(0, gzipInflatingBuffer.inflateBytes(b, 0, 1)); 405 assertEquals(zeroTerminatedBytes.length, gzipInflatingBuffer.getAndResetBytesConsumed()); 406 407 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(headerCrc16)); 408 assertEquals(0, gzipInflatingBuffer.inflateBytes(b, 0, 1)); 409 assertEquals(headerCrc16.length, gzipInflatingBuffer.getAndResetBytesConsumed()); 410 411 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(deflatedBytes)); 412 assertEquals(originalData.length, gzipInflatingBuffer.inflateBytes(b, 0, originalData.length)); 413 assertEquals(deflatedBytes.length, gzipInflatingBuffer.getAndResetBytesConsumed()); 414 415 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzipTrailer)); 416 assertEquals(0, gzipInflatingBuffer.inflateBytes(b, 0, 1)); 417 assertEquals(gzipTrailer.length, gzipInflatingBuffer.getAndResetBytesConsumed()); 418 } 419 420 @Test getAndResetDeflatedBytesConsumedExcludesGzipMetadata()421 public void getAndResetDeflatedBytesConsumedExcludesGzipMetadata() throws Exception { 422 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzippedData)); 423 424 byte[] b = new byte[originalData.length]; 425 assertEquals(originalData.length, gzipInflatingBuffer.inflateBytes(b, 0, originalData.length)); 426 assertEquals( 427 gzippedData.length - GZIP_HEADER_MIN_SIZE - GZIP_TRAILER_SIZE, 428 gzipInflatingBuffer.getAndResetDeflatedBytesConsumed()); 429 } 430 431 @Test wrongHeaderMagicShouldFail()432 public void wrongHeaderMagicShouldFail() throws Exception { 433 gzipHeader[1] = (byte) ~(GZIP_MAGIC >> 8); 434 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzipHeader)); 435 try { 436 byte[] b = new byte[1]; 437 gzipInflatingBuffer.inflateBytes(b, 0, 1); 438 fail("Expected ZipException"); 439 } catch (ZipException expectedException) { 440 assertEquals("Not in GZIP format", expectedException.getMessage()); 441 } 442 } 443 444 @Test wrongHeaderCompressionMethodShouldFail()445 public void wrongHeaderCompressionMethodShouldFail() throws Exception { 446 gzipHeader[2] = 7; // Should be 8 447 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzipHeader)); 448 try { 449 byte[] b = new byte[1]; 450 gzipInflatingBuffer.inflateBytes(b, 0, 1); 451 fail("Expected ZipException"); 452 } catch (ZipException expectedException) { 453 assertEquals("Unsupported compression method", expectedException.getMessage()); 454 } 455 } 456 457 @Test allHeaderFlagsWork()458 public void allHeaderFlagsWork() throws Exception { 459 gzipHeader[GZIP_HEADER_FLAG_INDEX] = 460 (byte) (gzipHeader[GZIP_HEADER_FLAG_INDEX] | FTEXT | FHCRC | FEXTRA | FNAME | FCOMMENT); 461 int len = 1025; 462 byte[] fExtraLen = {(byte) len, (byte) (len >> 8)}; 463 byte[] fExtra = new byte[len]; 464 byte[] zeroTerminatedBytes = new byte[len]; 465 for (int i = 0; i < len - 1; i++) { 466 zeroTerminatedBytes[i] = 1; 467 } 468 ByteArrayOutputStream newHeader = new ByteArrayOutputStream(); 469 newHeader.write(gzipHeader); 470 newHeader.write(fExtraLen); 471 newHeader.write(fExtra); 472 newHeader.write(zeroTerminatedBytes); // FNAME 473 newHeader.write(zeroTerminatedBytes); // FCOMMENT 474 byte[] headerCrc16 = getHeaderCrc16Bytes(newHeader.toByteArray()); 475 newHeader.write(headerCrc16); 476 477 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(newHeader.toByteArray())); 478 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(deflatedBytes)); 479 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzipTrailer)); 480 481 byte[] b = new byte[originalData.length]; 482 gzipInflatingBuffer.inflateBytes(b, 0, originalData.length); 483 assertTrue("inflated data does not match", Arrays.equals(originalData, b)); 484 } 485 486 @Test headerFTextFlagIsIgnored()487 public void headerFTextFlagIsIgnored() throws Exception { 488 gzipHeader[GZIP_HEADER_FLAG_INDEX] = (byte) (gzipHeader[GZIP_HEADER_FLAG_INDEX] | FTEXT); 489 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzipHeader)); 490 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(deflatedBytes)); 491 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzipTrailer)); 492 493 byte[] b = new byte[originalData.length]; 494 assertEquals(originalData.length, gzipInflatingBuffer.inflateBytes(b, 0, originalData.length)); 495 assertEquals(gzippedData.length, gzipInflatingBuffer.getAndResetBytesConsumed()); 496 assertTrue("inflated data does not match", Arrays.equals(originalData, b)); 497 } 498 499 @Test headerFhcrcFlagWorks()500 public void headerFhcrcFlagWorks() throws Exception { 501 gzipHeader[GZIP_HEADER_FLAG_INDEX] = (byte) (gzipHeader[GZIP_HEADER_FLAG_INDEX] | FHCRC); 502 503 byte[] headerCrc16 = getHeaderCrc16Bytes(gzipHeader); 504 505 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzipHeader)); 506 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(headerCrc16)); 507 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(deflatedBytes)); 508 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzipTrailer)); 509 510 byte[] b = new byte[originalData.length]; 511 assertEquals(originalData.length, gzipInflatingBuffer.inflateBytes(b, 0, originalData.length)); 512 assertEquals( 513 gzippedData.length + headerCrc16.length, gzipInflatingBuffer.getAndResetBytesConsumed()); 514 assertTrue("inflated data does not match", Arrays.equals(originalData, b)); 515 } 516 517 @Test headerInvalidFhcrcFlagFails()518 public void headerInvalidFhcrcFlagFails() throws Exception { 519 gzipHeader[GZIP_HEADER_FLAG_INDEX] = (byte) (gzipHeader[GZIP_HEADER_FLAG_INDEX] | FHCRC); 520 521 byte[] headerCrc16 = getHeaderCrc16Bytes(gzipHeader); 522 headerCrc16[0] = (byte) ~headerCrc16[0]; 523 524 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzipHeader)); 525 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(headerCrc16)); 526 try { 527 byte[] b = new byte[1]; 528 gzipInflatingBuffer.inflateBytes(b, 0, 1); 529 fail("Expected ZipException"); 530 } catch (ZipException expectedException) { 531 assertEquals("Corrupt GZIP header", expectedException.getMessage()); 532 } 533 } 534 535 @Test headerFExtraFlagWorks()536 public void headerFExtraFlagWorks() throws Exception { 537 gzipHeader[GZIP_HEADER_FLAG_INDEX] = (byte) (gzipHeader[GZIP_HEADER_FLAG_INDEX] | FEXTRA); 538 539 int len = 1025; 540 byte[] fExtraLen = {(byte) len, (byte) (len >> 8)}; 541 byte[] fExtra = new byte[len]; 542 543 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzipHeader)); 544 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(fExtraLen)); 545 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(fExtra)); 546 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(deflatedBytes)); 547 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzipTrailer)); 548 549 byte[] b = new byte[originalData.length]; 550 assertEquals(originalData.length, gzipInflatingBuffer.inflateBytes(b, 0, originalData.length)); 551 assertEquals( 552 gzippedData.length + fExtraLen.length + fExtra.length, 553 gzipInflatingBuffer.getAndResetBytesConsumed()); 554 assertTrue("inflated data does not match", Arrays.equals(originalData, b)); 555 } 556 557 @Test headerFExtraFlagWithZeroLenWorks()558 public void headerFExtraFlagWithZeroLenWorks() throws Exception { 559 gzipHeader[GZIP_HEADER_FLAG_INDEX] = (byte) (gzipHeader[GZIP_HEADER_FLAG_INDEX] | FEXTRA); 560 byte[] fExtraLen = new byte[2]; 561 562 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzipHeader)); 563 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(fExtraLen)); 564 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(deflatedBytes)); 565 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzipTrailer)); 566 567 byte[] b = new byte[originalData.length]; 568 assertEquals(originalData.length, gzipInflatingBuffer.inflateBytes(b, 0, originalData.length)); 569 assertEquals( 570 gzippedData.length + fExtraLen.length, gzipInflatingBuffer.getAndResetBytesConsumed()); 571 assertTrue("inflated data does not match", Arrays.equals(originalData, b)); 572 } 573 574 @Test headerFExtraFlagWithMissingExtraLenFails()575 public void headerFExtraFlagWithMissingExtraLenFails() throws Exception { 576 gzipHeader[GZIP_HEADER_FLAG_INDEX] = (byte) (gzipHeader[GZIP_HEADER_FLAG_INDEX] | FEXTRA); 577 578 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzipHeader)); 579 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(deflatedBytes)); 580 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzipTrailer)); 581 582 try { 583 byte[] b = new byte[originalData.length]; 584 gzipInflatingBuffer.inflateBytes(b, 0, originalData.length); 585 fail("Expected DataFormatException"); 586 } catch (DataFormatException expectedException) { 587 assertTrue( 588 "wrong exception message", 589 expectedException.getMessage().startsWith("Inflater data format exception:")); 590 } 591 } 592 593 @Test headerFExtraFlagWithMissingExtraBytesFails()594 public void headerFExtraFlagWithMissingExtraBytesFails() throws Exception { 595 gzipHeader[GZIP_HEADER_FLAG_INDEX] = (byte) (gzipHeader[GZIP_HEADER_FLAG_INDEX] | FEXTRA); 596 597 int len = 5; 598 byte[] fExtraLen = {(byte) len, (byte) (len >> 8)}; 599 600 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzipHeader)); 601 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(fExtraLen)); 602 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(deflatedBytes)); 603 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzipTrailer)); 604 605 try { 606 byte[] b = new byte[originalData.length]; 607 gzipInflatingBuffer.inflateBytes(b, 0, originalData.length); 608 fail("Expected DataFormatException"); 609 } catch (DataFormatException expectedException) { 610 assertTrue( 611 "wrong exception message", 612 expectedException.getMessage().startsWith("Inflater data format exception:")); 613 } 614 } 615 616 @Test headerFNameFlagWorks()617 public void headerFNameFlagWorks() throws Exception { 618 gzipHeader[GZIP_HEADER_FLAG_INDEX] = (byte) (gzipHeader[GZIP_HEADER_FLAG_INDEX] | FNAME); 619 int len = 1025; 620 byte[] zeroTerminatedBytes = new byte[len]; 621 for (int i = 0; i < len - 1; i++) { 622 zeroTerminatedBytes[i] = 1; 623 } 624 625 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzipHeader)); 626 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(zeroTerminatedBytes)); 627 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(deflatedBytes)); 628 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzipTrailer)); 629 630 byte[] b = new byte[originalData.length]; 631 assertEquals(originalData.length, gzipInflatingBuffer.inflateBytes(b, 0, originalData.length)); 632 assertEquals(gzippedData.length + len, gzipInflatingBuffer.getAndResetBytesConsumed()); 633 assertTrue("inflated data does not match", Arrays.equals(originalData, b)); 634 } 635 636 @Test headerFNameFlagWithMissingBytesFail()637 public void headerFNameFlagWithMissingBytesFail() throws Exception { 638 gzipHeader[GZIP_HEADER_FLAG_INDEX] = (byte) (gzipHeader[GZIP_HEADER_FLAG_INDEX] | FNAME); 639 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzipHeader)); 640 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(deflatedBytes)); 641 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzipTrailer)); 642 643 try { 644 byte[] b = new byte[originalData.length]; 645 gzipInflatingBuffer.inflateBytes(b, 0, originalData.length); 646 fail("Expected DataFormatException"); 647 } catch (DataFormatException expectedException) { 648 assertTrue( 649 "wrong exception message", 650 expectedException.getMessage().startsWith("Inflater data format exception:")); 651 } 652 } 653 654 @Test headerFCommentFlagWorks()655 public void headerFCommentFlagWorks() throws Exception { 656 gzipHeader[GZIP_HEADER_FLAG_INDEX] = (byte) (gzipHeader[GZIP_HEADER_FLAG_INDEX] | FCOMMENT); 657 int len = 1025; 658 byte[] zeroTerminatedBytes = new byte[len]; 659 for (int i = 0; i < len - 1; i++) { 660 zeroTerminatedBytes[i] = 1; 661 } 662 663 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzipHeader)); 664 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(zeroTerminatedBytes)); 665 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(deflatedBytes)); 666 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzipTrailer)); 667 668 byte[] b = new byte[originalData.length]; 669 assertEquals(originalData.length, gzipInflatingBuffer.inflateBytes(b, 0, originalData.length)); 670 assertEquals(gzippedData.length + len, gzipInflatingBuffer.getAndResetBytesConsumed()); 671 assertTrue("inflated data does not match", Arrays.equals(originalData, b)); 672 } 673 674 @Test headerFCommentFlagWithMissingBytesFail()675 public void headerFCommentFlagWithMissingBytesFail() throws Exception { 676 gzipHeader[GZIP_HEADER_FLAG_INDEX] = (byte) (gzipHeader[GZIP_HEADER_FLAG_INDEX] | FCOMMENT); 677 678 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzipHeader)); 679 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(deflatedBytes)); 680 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzipTrailer)); 681 try { 682 byte[] b = new byte[originalData.length]; 683 gzipInflatingBuffer.inflateBytes(b, 0, originalData.length); 684 fail("Expected DataFormatException"); 685 } catch (DataFormatException expectedException) { 686 assertTrue( 687 "wrong exception message", 688 expectedException.getMessage().startsWith("Inflater data format exception:")); 689 } 690 } 691 692 @Test wrongTrailerCrcShouldFail()693 public void wrongTrailerCrcShouldFail() throws Exception { 694 gzipTrailer[0] = (byte) ~gzipTrailer[0]; 695 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzipHeader)); 696 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(deflatedBytes)); 697 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzipTrailer)); 698 699 try { 700 byte[] b = new byte[originalData.length]; 701 gzipInflatingBuffer.inflateBytes(b, 0, originalData.length); 702 fail("Expected ZipException"); 703 } catch (ZipException expectedException) { 704 assertEquals("Corrupt GZIP trailer", expectedException.getMessage()); 705 } 706 } 707 708 @Test wrongTrailerISizeShouldFail()709 public void wrongTrailerISizeShouldFail() throws Exception { 710 gzipTrailer[GZIP_TRAILER_SIZE - 1] = (byte) ~gzipTrailer[GZIP_TRAILER_SIZE - 1]; 711 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzipHeader)); 712 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(deflatedBytes)); 713 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzipTrailer)); 714 715 try { 716 byte[] b = new byte[originalData.length]; 717 gzipInflatingBuffer.inflateBytes(b, 0, originalData.length); 718 fail("Expected ZipException"); 719 } catch (ZipException expectedException) { 720 assertEquals("Corrupt GZIP trailer", expectedException.getMessage()); 721 } 722 } 723 724 @Test invalidDeflateBlockShouldFail()725 public void invalidDeflateBlockShouldFail() throws Exception { 726 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(gzipHeader)); 727 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(new byte[10])); 728 729 try { 730 byte[] b = new byte[originalData.length]; 731 gzipInflatingBuffer.inflateBytes(b, 0, originalData.length); 732 fail("Expected DataFormatException"); 733 } catch (DataFormatException expectedException) { 734 assertTrue( 735 "wrong exception message", 736 expectedException.getMessage().startsWith("Inflater data format exception:")); 737 } 738 } 739 addInTwoChunksAndVerifyIsStalled(byte[] input)740 private void addInTwoChunksAndVerifyIsStalled(byte[] input) throws Exception { 741 byte[] b = new byte[1]; 742 743 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(input, 0, input.length - 1)); 744 assertFalse("gzipInflatingBuffer is stalled", gzipInflatingBuffer.isStalled()); 745 746 assertEquals(0, gzipInflatingBuffer.inflateBytes(b, 0, 1)); 747 assertTrue("gzipInflatingBuffer is not stalled", gzipInflatingBuffer.isStalled()); 748 749 gzipInflatingBuffer.addGzippedBytes(ReadableBuffers.wrap(input, input.length - 1, 1)); 750 assertFalse("gzipInflatingBuffer is stalled", gzipInflatingBuffer.isStalled()); 751 752 assertEquals(0, gzipInflatingBuffer.inflateBytes(b, 0, 1)); 753 assertTrue("gzipInflatingBuffer is not stalled", gzipInflatingBuffer.isStalled()); 754 } 755 getHeaderCrc16Bytes(byte[] headerBytes)756 private byte[] getHeaderCrc16Bytes(byte[] headerBytes) { 757 CRC32 crc = new CRC32(); 758 crc.update(headerBytes); 759 byte[] headerCrc16 = {(byte) crc.getValue(), (byte) (crc.getValue() >> 8)}; 760 return headerCrc16; 761 } 762 } 763