1069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project/*
2069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/io/ChunkedInputStream.java $
3069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project * $Revision: 569843 $
4069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project * $Date: 2007-08-26 10:05:40 -0700 (Sun, 26 Aug 2007) $
5069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project *
6069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project * ====================================================================
7069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project * Licensed to the Apache Software Foundation (ASF) under one
8069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project * or more contributor license agreements.  See the NOTICE file
9069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project * distributed with this work for additional information
10069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project * regarding copyright ownership.  The ASF licenses this file
11069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project * to you under the Apache License, Version 2.0 (the
12069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project * "License"); you may not use this file except in compliance
13069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project * with the License.  You may obtain a copy of the License at
14069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project *
15069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project *   http://www.apache.org/licenses/LICENSE-2.0
16069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project *
17069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project * Unless required by applicable law or agreed to in writing,
18069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project * software distributed under the License is distributed on an
19069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
20069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project * KIND, either express or implied.  See the License for the
21069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project * specific language governing permissions and limitations
22069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project * under the License.
23069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project * ====================================================================
24069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project *
25069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project * This software consists of voluntary contributions made by many
26069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project * individuals on behalf of the Apache Software Foundation.  For more
27069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project * information on the Apache Software Foundation, please see
28069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project * <http://www.apache.org/>.
29069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project *
30069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project */
31069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project
32069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Projectpackage org.apache.http.impl.io;
33069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project
34069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Projectimport java.io.IOException;
35069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Projectimport java.io.InputStream;
36069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project
37069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Projectimport org.apache.http.Header;
38069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Projectimport org.apache.http.HttpException;
39069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Projectimport org.apache.http.MalformedChunkCodingException;
40069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Projectimport org.apache.http.io.SessionInputBuffer;
41069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Projectimport org.apache.http.protocol.HTTP;
42069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Projectimport org.apache.http.util.CharArrayBuffer;
43069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Projectimport org.apache.http.util.ExceptionUtils;
44069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project
45069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project/**
46069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project * Implements chunked transfer coding.
47069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project * See <a href="http://www.w3.org/Protocols/rfc2616/rfc2616.txt">RFC 2616</a>,
48069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6">section 3.6.1</a>.
49069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project * It transparently coalesces chunks of a HTTP stream that uses chunked
50069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project * transfer coding. After the stream is read to the end, it provides access
51069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project * to the trailers, if any.
52069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project * <p>
53069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project * Note that this class NEVER closes the underlying stream, even when close
54069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project * gets called.  Instead, it will read until the "end" of its chunking on
55069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project * close, which allows for the seamless execution of subsequent HTTP 1.1
56069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project * requests, while not requiring the client to remember to read the entire
57069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project * contents of the response.
58069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project * </p>
59069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project *
60069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project * @author Ortwin Glueck
61069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project * @author Sean C. Sullivan
62069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project * @author Martin Elwin
63069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project * @author Eric Johnson
64069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
65069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project * @author Michael Becke
66069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
67069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project *
68069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project * @since 4.0
69069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project *
70069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project */
71069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Projectpublic class ChunkedInputStream extends InputStream {
72069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project
73069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    /** The session input buffer */
74069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    private SessionInputBuffer in;
75069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project
76069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    private final CharArrayBuffer buffer;
77069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project
78069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    /** The chunk size */
79069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    private int chunkSize;
80069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project
81069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    /** The current position within the current chunk */
82069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    private int pos;
83069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project
84069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    /** True if we'are at the beginning of stream */
85069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    private boolean bof = true;
86069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project
87069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    /** True if we've reached the end of stream */
88069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    private boolean eof = false;
89069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project
90069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    /** True if this stream is closed */
91069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    private boolean closed = false;
92069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project
93069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    private Header[] footers = new Header[] {};
94069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project
95069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    public ChunkedInputStream(final SessionInputBuffer in) {
96069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        super();
97069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        if (in == null) {
98069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project            throw new IllegalArgumentException("Session input buffer may not be null");
99069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        }
100069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        this.in = in;
101069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        this.pos = 0;
102069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        this.buffer = new CharArrayBuffer(16);
103069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    }
104069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project
105069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    /**
106069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * <p> Returns all the data in a chunked stream in coalesced form. A chunk
107069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * is followed by a CRLF. The method returns -1 as soon as a chunksize of 0
108069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * is detected.</p>
109069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     *
110069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * <p> Trailer headers are read automcatically at the end of the stream and
111069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * can be obtained with the getResponseFooters() method.</p>
112069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     *
113069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * @return -1 of the end of the stream has been reached or the next data
114069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * byte
115069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * @throws IOException If an IO problem occurs
116069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     */
117069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    public int read() throws IOException {
118069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        if (this.closed) {
119069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project            throw new IOException("Attempted read from closed stream.");
120069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        }
121069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        if (this.eof) {
122069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project            return -1;
123069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        }
124069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        if (this.pos >= this.chunkSize) {
125069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project            nextChunk();
126069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project            if (this.eof) {
127069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                return -1;
128069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project            }
129069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        }
130069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        pos++;
131069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        return in.read();
132069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    }
133069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project
134069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    /**
135069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * Read some bytes from the stream.
136069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * @param b The byte array that will hold the contents from the stream.
137069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * @param off The offset into the byte array at which bytes will start to be
138069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * placed.
139069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * @param len the maximum number of bytes that can be returned.
140069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * @return The number of bytes returned or -1 if the end of stream has been
141069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * reached.
142069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * @see java.io.InputStream#read(byte[], int, int)
143069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * @throws IOException if an IO problem occurs.
144069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     */
145069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    public int read (byte[] b, int off, int len) throws IOException {
146069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project
147069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        if (closed) {
148069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project            throw new IOException("Attempted read from closed stream.");
149069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        }
150069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project
151069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        if (eof) {
152069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project            return -1;
153069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        }
154069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        if (pos >= chunkSize) {
155069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project            nextChunk();
156069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project            if (eof) {
157069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                return -1;
158069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project            }
159069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        }
160069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        len = Math.min(len, chunkSize - pos);
161069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        int count = in.read(b, off, len);
162069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        pos += count;
163069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        return count;
164069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    }
165069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project
166069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    /**
167069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * Read some bytes from the stream.
168069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * @param b The byte array that will hold the contents from the stream.
169069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * @return The number of bytes returned or -1 if the end of stream has been
170069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * reached.
171069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * @see java.io.InputStream#read(byte[])
172069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * @throws IOException if an IO problem occurs.
173069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     */
174069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    public int read (byte[] b) throws IOException {
175069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        return read(b, 0, b.length);
176069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    }
177069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project
178069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    /**
179069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * Read the next chunk.
180069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * @throws IOException If an IO error occurs.
181069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     */
182069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    private void nextChunk() throws IOException {
183069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        chunkSize = getChunkSize();
184069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        if (chunkSize < 0) {
185069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project            throw new MalformedChunkCodingException("Negative chunk size");
186069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        }
187069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        bof = false;
188069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        pos = 0;
189069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        if (chunkSize == 0) {
190069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project            eof = true;
191069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project            parseTrailerHeaders();
192069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        }
193069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    }
194069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project
195069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    /**
196069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * Expects the stream to start with a chunksize in hex with optional
197069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * comments after a semicolon. The line must end with a CRLF: "a3; some
198069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * comment\r\n" Positions the stream at the start of the next line.
199069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     *
200069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * @param in The new input stream.
201069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * @param required <tt>true<tt/> if a valid chunk must be present,
202069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     *                 <tt>false<tt/> otherwise.
203069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     *
204069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * @return the chunk size as integer
205069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     *
206069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * @throws IOException when the chunk size could not be parsed
207069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     */
208069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    private int getChunkSize() throws IOException {
209069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        // skip CRLF
210069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        if (!bof) {
211069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project            int cr = in.read();
212069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project            int lf = in.read();
213069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project            if ((cr != HTTP.CR) || (lf != HTTP.LF)) {
214069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                throw new MalformedChunkCodingException(
215069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    "CRLF expected at end of chunk");
216069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project            }
217069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        }
218069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        //parse data
219069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        this.buffer.clear();
220069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        int i = this.in.readLine(this.buffer);
221069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        if (i == -1) {
222069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project            throw new MalformedChunkCodingException(
223069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    "Chunked stream ended unexpectedly");
224069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        }
225069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        int separator = this.buffer.indexOf(';');
226069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        if (separator < 0) {
227069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project            separator = this.buffer.length();
228069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        }
229069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        try {
230069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project            return Integer.parseInt(this.buffer.substringTrimmed(0, separator), 16);
231069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        } catch (NumberFormatException e) {
232069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project            throw new MalformedChunkCodingException("Bad chunk header");
233069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        }
234069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    }
235069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project
236069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    /**
237069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * Reads and stores the Trailer headers.
238069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * @throws IOException If an IO problem occurs
239069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     */
240069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    private void parseTrailerHeaders() throws IOException {
241069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        try {
242069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project            this.footers = AbstractMessageParser.parseHeaders
243069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                (in, -1, -1, null);
244069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        } catch (HttpException e) {
245069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project            IOException ioe = new MalformedChunkCodingException("Invalid footer: "
246069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    + e.getMessage());
247069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project            ExceptionUtils.initCause(ioe, e);
248069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project            throw ioe;
249069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        }
250069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    }
251069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project
252069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    /**
253069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * Upon close, this reads the remainder of the chunked message,
254069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * leaving the underlying socket at a position to start reading the
255069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * next response without scanning.
256069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * @throws IOException If an IO problem occurs.
257069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     */
258069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    public void close() throws IOException {
259069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        if (!closed) {
260069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project            try {
261069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                if (!eof) {
262069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    exhaustInputStream(this);
263069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                }
264069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project            } finally {
265069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                eof = true;
266069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                closed = true;
267069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project            }
268069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        }
269069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    }
270069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project
271069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    public Header[] getFooters() {
272069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        return (Header[])this.footers.clone();
273069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    }
274069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project
275069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    /**
276069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * Exhaust an input stream, reading until EOF has been encountered.
277069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     *
278069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * <p>Note that this function is intended as a non-public utility.
279069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * This is a little weird, but it seemed silly to make a utility
280069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * class for this one function, so instead it is just static and
281069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * shared that way.</p>
282069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     *
283069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * @param inStream The {@link InputStream} to exhaust.
284069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * @throws IOException If an IO problem occurs
285069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     */
286069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    static void exhaustInputStream(final InputStream inStream) throws IOException {
287069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        // read and discard the remainder of the message
288069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        byte buffer[] = new byte[1024];
289069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        while (inStream.read(buffer) >= 0) {
290069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project            ;
291069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        }
292069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    }
293069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project
294069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project}
295