001/*
002 * ====================================================================
003 * Licensed to the Apache Software Foundation (ASF) under one
004 * or more contributor license agreements.  See the NOTICE file
005 * distributed with this work for additional information
006 * regarding copyright ownership.  The ASF licenses this file
007 * to you under the Apache License, Version 2.0 (the
008 * "License"); you may not use this file except in compliance
009 * with the License.  You may obtain a copy of the License at
010 *
011 *   http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing,
014 * software distributed under the License is distributed on an
015 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
016 * KIND, either express or implied.  See the License for the
017 * specific language governing permissions and limitations
018 * under the License.
019 * ====================================================================
020 *
021 * This software consists of voluntary contributions made by many
022 * individuals on behalf of the Apache Software Foundation.  For more
023 * information on the Apache Software Foundation, please see
024 * <http://www.apache.org/>.
025 *
026 */
027package org.apache.http.client.entity;
028
029import java.io.IOException;
030import java.io.InputStream;
031import java.io.PushbackInputStream;
032import java.util.zip.Inflater;
033import java.util.zip.InflaterInputStream;
034import java.util.zip.ZipException;
035
036/**
037 * Deflate input stream.    This class includes logic needed for various Rfc's in order
038 * to reasonably implement the "deflate" compression style.
039 */
040public class DeflateInputStream extends InputStream {
041
042    private final InputStream sourceStream;
043
044    public DeflateInputStream(final InputStream wrapped) throws IOException {
045
046        final PushbackInputStream pushback = new PushbackInputStream(wrapped, 2);
047        final int i1 = pushback.read();
048        final int i2 = pushback.read();
049        if (i1 == -1 || i2 == -1) {
050            throw new ZipException("Unexpected end of stream");
051        }
052
053        pushback.unread(i2);
054        pushback.unread(i1);
055
056        boolean nowrap = true;
057        final int b1 = i1 & 0xFF;
058        final int compressionMethod = b1 & 0xF;
059        final int compressionInfo = b1 >> 4 & 0xF;
060        final int b2 = i2 & 0xFF;
061        if (compressionMethod == 8 && compressionInfo <= 7 && ((b1 << 8) | b2) % 31 == 0) {
062            nowrap = false;
063        }
064        sourceStream = new DeflateStream(pushback, new Inflater(nowrap));
065    }
066
067    /**
068     * Read a byte.
069     */
070    @Override
071    public int read() throws IOException {
072        return sourceStream.read();
073    }
074
075    /**
076     * Read lots of bytes.
077     */
078    @Override
079    public int read(final byte[] b) throws IOException {
080        return sourceStream.read(b);
081    }
082
083    /**
084     * Read lots of specific bytes.
085     */
086    @Override
087    public int read(final byte[] b, final int off, final int len) throws IOException {
088        return sourceStream.read(b, off, len);
089    }
090
091    /**
092     * Skip
093     */
094    @Override
095    public long skip(final long n) throws IOException {
096        return sourceStream.skip(n);
097    }
098
099    /**
100     * Get available.
101     */
102    @Override
103    public int available() throws IOException {
104        return sourceStream.available();
105    }
106
107    /**
108     * Mark.
109     */
110    @Override
111    public void mark(final int readLimit) {
112        sourceStream.mark(readLimit);
113    }
114
115    /**
116     * Reset.
117     */
118    @Override
119    public void reset() throws IOException {
120        sourceStream.reset();
121    }
122
123    /**
124     * Check if mark is supported.
125     */
126    @Override
127    public boolean markSupported() {
128        return sourceStream.markSupported();
129    }
130
131    /**
132     * Close.
133     */
134    @Override
135    public void close() throws IOException {
136        sourceStream.close();
137    }
138
139    static class DeflateStream extends InflaterInputStream {
140
141        private boolean closed = false;
142
143        public DeflateStream(final InputStream in, final Inflater inflater) {
144            super(in, inflater);
145        }
146
147        @Override
148        public void close() throws IOException {
149            if (closed) {
150                return;
151            }
152            closed = true;
153            inf.end();
154            super.close();
155        }
156
157    }
158
159}
160