13c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller/*
23c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * Copyright (C) 2014 Square, Inc.
33c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller *
43c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * Licensed under the Apache License, Version 2.0 (the "License");
53c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * you may not use this file except in compliance with the License.
63c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * You may obtain a copy of the License at
73c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller *
83c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller *      http://www.apache.org/licenses/LICENSE-2.0
93c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller *
103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * Unless required by applicable law or agreed to in writing, software
113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * distributed under the License is distributed on an "AS IS" BASIS,
123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
133c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * See the License for the specific language governing permissions and
143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * limitations under the License.
153c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller */
163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerpackage okio;
173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.io.IOException;
193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.util.zip.CRC32;
203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport org.junit.Test;
213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport static okio.Util.UTF_8;
233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport static org.junit.Assert.assertEquals;
243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport static org.junit.Assert.assertFalse;
253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport static org.junit.Assert.assertTrue;
263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport static org.junit.Assert.fail;
273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerpublic class GzipSourceTest {
293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Test public void gunzip() throws Exception {
313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    OkBuffer gzipped = new OkBuffer();
323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    gzipped.write(gzipHeader);
333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    gzipped.write(deflated);
343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    gzipped.write(gzipTrailer);
353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertGzipped(gzipped);
363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Test public void gunzip_withHCRC() throws Exception {
393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    CRC32 hcrc = new CRC32();
403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    ByteString gzipHeader = gzipHeaderWithFlags((byte) 0x02);
413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    hcrc.update(gzipHeader.toByteArray());
423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    OkBuffer gzipped = new OkBuffer();
443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    gzipped.write(gzipHeader);
453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    gzipped.writeShort(Util.reverseBytesShort((short) hcrc.getValue())); // little endian
463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    gzipped.write(deflated);
473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    gzipped.write(gzipTrailer);
483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertGzipped(gzipped);
493c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
513c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Test public void gunzip_withExtra() throws Exception {
523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    OkBuffer gzipped = new OkBuffer();
533c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    gzipped.write(gzipHeaderWithFlags((byte) 0x04));
543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    gzipped.writeShort(Util.reverseBytesShort((short) 7)); // little endian extra length
553c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    gzipped.write("blubber".getBytes(UTF_8), 0, 7);
563c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    gzipped.write(deflated);
573c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    gzipped.write(gzipTrailer);
583c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertGzipped(gzipped);
593c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
603c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
613c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Test public void gunzip_withName() throws Exception {
623c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    OkBuffer gzipped = new OkBuffer();
633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    gzipped.write(gzipHeaderWithFlags((byte) 0x08));
643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    gzipped.write("foo.txt".getBytes(UTF_8), 0, 7);
653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    gzipped.writeByte(0); // zero-terminated
663c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    gzipped.write(deflated);
673c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    gzipped.write(gzipTrailer);
683c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertGzipped(gzipped);
693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
703c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
713c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Test public void gunzip_withComment() throws Exception {
723c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    OkBuffer gzipped = new OkBuffer();
733c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    gzipped.write(gzipHeaderWithFlags((byte) 0x10));
743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    gzipped.write("rubbish".getBytes(UTF_8), 0, 7);
753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    gzipped.writeByte(0); // zero-terminated
763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    gzipped.write(deflated);
773c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    gzipped.write(gzipTrailer);
783c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertGzipped(gzipped);
793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
813c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /**
823c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * For portability, it is a good idea to export the gzipped bytes and try running gzip.  Ex.
833c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * {@code echo gzipped | base64 --decode | gzip -l -v}
843c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   */
853c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Test public void gunzip_withAll() throws Exception {
863c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    OkBuffer gzipped = new OkBuffer();
873c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    gzipped.write(gzipHeaderWithFlags((byte) 0x1c));
883c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    gzipped.writeShort(Util.reverseBytesShort((short) 7)); // little endian extra length
893c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    gzipped.write("blubber".getBytes(UTF_8), 0, 7);
903c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    gzipped.write("foo.txt".getBytes(UTF_8), 0, 7);
913c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    gzipped.writeByte(0); // zero-terminated
923c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    gzipped.write("rubbish".getBytes(UTF_8), 0, 7);
933c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    gzipped.writeByte(0); // zero-terminated
943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    gzipped.write(deflated);
953c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    gzipped.write(gzipTrailer);
963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertGzipped(gzipped);
973c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
983c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
993c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private void assertGzipped(OkBuffer gzipped) throws IOException {
1003c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    OkBuffer gunzipped = gunzip(gzipped);
1013c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals("It's a UNIX system! I know this!", gunzipped.readUtf8(gunzipped.size()));
1023c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
1033c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1043c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /**
1053c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * Note that you cannot test this with old versions of gzip, as they interpret flag bit 1 as
1063c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * CONTINUATION, not HCRC. For example, this is the case with the default gzip on osx.
1073c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   */
1083c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Test public void gunzipWhenHeaderCRCIncorrect() throws Exception {
1093c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    OkBuffer gzipped = new OkBuffer();
1103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    gzipped.write(gzipHeaderWithFlags((byte) 0x02));
1113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    gzipped.writeShort((short) 0); // wrong HCRC!
1123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    gzipped.write(deflated);
1133c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    gzipped.write(gzipTrailer);
1143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1153c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    try {
1163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      gunzip(gzipped);
1173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      fail();
1183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    } catch (IOException e) {
1193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      assertEquals("FHCRC: actual 0x0000261d != expected 0x00000000", e.getMessage());
1203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
1213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
1223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Test public void gunzipWhenCRCIncorrect() throws Exception {
1243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    OkBuffer gzipped = new OkBuffer();
1253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    gzipped.write(gzipHeader);
1263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    gzipped.write(deflated);
1273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    gzipped.writeInt(Util.reverseBytesInt(0x1234567)); // wrong CRC
1283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    gzipped.write(gzipTrailer.toByteArray(), 3, 4);
1293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    try {
1313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      gunzip(gzipped);
1323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      fail();
1333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    } catch (IOException e) {
1343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      assertEquals("CRC: actual 0x37ad8f8d != expected 0x01234567", e.getMessage());
1353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
1363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
1373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Test public void gunzipWhenLengthIncorrect() throws Exception {
1393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    OkBuffer gzipped = new OkBuffer();
1403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    gzipped.write(gzipHeader);
1413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    gzipped.write(deflated);
1423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    gzipped.write(gzipTrailer.toByteArray(), 0, 4);
1433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    gzipped.writeInt(Util.reverseBytesInt(0x123456)); // wrong length
1443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    try {
1463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      gunzip(gzipped);
1473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      fail();
1483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    } catch (IOException e) {
1493c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      assertEquals("ISIZE: actual 0x00000020 != expected 0x00123456", e.getMessage());
1503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
1513c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
1523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1533c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Test public void gunzipExhaustsSource() throws Exception {
1543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    OkBuffer gzippedSource = new OkBuffer()
1553c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        .write(ByteString.decodeHex("1f8b08000000000000004b4c4a0600c241243503000000")); // 'abc'
1563c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1573c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    ExhaustableSource exhaustableSource = new ExhaustableSource(gzippedSource);
1583c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    BufferedSource gunzippedSource = Okio.buffer(new GzipSource(exhaustableSource));
1593c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1603c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals('a', gunzippedSource.readByte());
1613c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals('b', gunzippedSource.readByte());
1623c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals('c', gunzippedSource.readByte());
1633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertFalse(exhaustableSource.exhausted);
1643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals(-1, gunzippedSource.read(new OkBuffer(), 1));
1653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertTrue(exhaustableSource.exhausted);
1663c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
1673c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1683c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Test public void gunzipThrowsIfSourceIsNotExhausted() throws Exception {
1693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    OkBuffer gzippedSource = new OkBuffer()
1703c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        .write(ByteString.decodeHex("1f8b08000000000000004b4c4a0600c241243503000000")); // 'abc'
1713c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    gzippedSource.writeByte('d'); // This byte shouldn't be here!
1723c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1733c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    BufferedSource gunzippedSource = Okio.buffer(new GzipSource(gzippedSource));
1743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals('a', gunzippedSource.readByte());
1763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals('b', gunzippedSource.readByte());
1773c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals('c', gunzippedSource.readByte());
1783c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    try {
1793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      gunzippedSource.readByte();
1803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      fail();
1813c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    } catch (IOException expected) {
1823c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
1833c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
1843c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1853c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private ByteString gzipHeaderWithFlags(byte flags) {
1863c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    byte[] result = gzipHeader.toByteArray();
1873c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    result[3] = flags;
1883c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return ByteString.of(result);
1893c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
1903c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1913c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private final ByteString gzipHeader = ByteString.decodeHex("1f8b0800000000000000");
1923c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1933c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  // Deflated "It's a UNIX system! I know this!"
1943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private final ByteString deflated = ByteString.decodeHex(
1953c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      "f32c512f56485408f5f38c5028ae2c2e49cd5554f054c8cecb2f5728c9c82c560400");
1963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1973c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private final ByteString gzipTrailer = ByteString.decodeHex(""
1983c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      + "8d8fad37" // Checksum of deflated.
1993c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      + "20000000" // 32 in little endian.
2003c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  );
2013c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
2023c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private OkBuffer gunzip(OkBuffer gzipped) throws IOException {
2033c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    OkBuffer result = new OkBuffer();
2043c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    GzipSource source = new GzipSource(gzipped);
2053c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    while (source.read(result, Integer.MAX_VALUE) != -1) {
2063c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
2073c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return result;
2083c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
2093c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
2103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /** This source keeps track of whether its read have returned -1. */
2113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  static class ExhaustableSource implements Source {
2123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    private final Source source;
2133c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    private boolean exhausted;
2143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
2153c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    ExhaustableSource(Source source) {
2163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      this.source = source;
2173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
2183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
2193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    @Override public long read(OkBuffer sink, long byteCount) throws IOException {
2203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      long result = source.read(sink, byteCount);
2213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      if (result == -1) exhausted = true;
2223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      return result;
2233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
2243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
2253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    @Override public Source deadline(Deadline deadline) {
2263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      source.deadline(deadline);
2273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      return this;
2283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
2293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
2303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    @Override public void close() throws IOException {
2313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      source.close();
2323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
2333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
2343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller}
235