chunkedinputstream.java

来自「This is a resource based on j2me embedde」· Java 代码 · 共 766 行 · 第 1/2 页

JAVA
766
字号
/* * @(#)ChunkedInputStream.java	1.11 06/10/10 * * Copyright  1990-2008 Sun Microsystems, Inc. All Rights Reserved.   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER   *    * This program is free software; you can redistribute it and/or   * modify it under the terms of the GNU General Public License version   * 2 only, as published by the Free Software Foundation.    *    * This program is distributed in the hope that it will be useful, but   * WITHOUT ANY WARRANTY; without even the implied warranty of   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU   * General Public License version 2 for more details (a copy is   * included at /legal/license.txt).    *    * You should have received a copy of the GNU General Public License   * version 2 along with this work; if not, write to the Free Software   * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA   * 02110-1301 USA    *    * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa   * Clara, CA 95054 or visit www.sun.com if you need additional   * information or have any questions.  * */package sun.net.www.http;import java.io.*;import java.util.*;import sun.net.www.*;/** * A <code>ChunkedInputStream</code> provides a stream for reading a body of * a http message that can be sent as a series of chunks, each with its own * size indicator. Optionally the last chunk can be followed by trailers  * containing entity-header fields. * <p> * A <code>ChunkedInputStream</code> is also <code>Hurryable</code> so it * can be hurried to the end of the stream if the bytes are available on * the underlying stream. */publicclass ChunkedInputStream extends InputStream implements Hurryable {    /**     * The underlying stream     */    private InputStream in;    /**     * The <code>HttpClient<code> that should be notified when the chunked stream has     * completed.     */    private HttpClient hc;    /**     * The <code>MessageHeader</code> that is populated with any optional trailer     * that appear after the last chunk.     */    private MessageHeader responses;    /**     * The size, in bytes, of the chunk that is currently being read.     * This size is only valid if the current position in the underlying     * input stream is inside a chunk (ie: state == STATE_READING_CHUNK).     */    private int chunkSize;    /**     * The number of bytes read from the underlying stream for the current     * chunk. This value is always in the range <code>0</code> through to     * <code>chunkSize</code>     */    private int chunkRead;    /**     * The internal buffer array where chunk data is available for the      * application to read.      */    private byte chunkData[] = new byte[4096];    /**     * The current position in the buffer. It contains the index     * of the next byte to read from <code>chunkData</code>     */    private int chunkPos;    /**     * The index one greater than the index of the last valid byte in the     * buffer. This value is always in the range <code>0</code> through      * <code>chunkData.length</code>.     */    private int chunkCount;    /**     * The internal buffer where bytes from the underlying stream can be     * read. It may contain bytes representing chunk-size, chunk-data, or      * trailer fields.      */    private byte rawData[] = new byte[32];    /**     * The current position in the buffer. It contains the index     * of the next byte to read from <code>rawData</code>     */    private int rawPos;    /**     * The index one greater than the index of the last valid byte in the     * buffer. This value is always in the range <code>0</code> through      * <code>rawData.length</code>.     */    private int rawCount;    /**     * Indicates if an error was encountered when processing the chunked     * stream.      */    private boolean error;    /**     * Indicates if the chunked stream has been closed using the      * <code>close</close> method.     */    private boolean closed;    /**     * State to indicate that next field should be :-     *	chunk-size [ chunk-extension ] CRLF     */    static final int STATE_AWAITING_CHUNK_HEADER    = 1;    /**     * State to indicate that we are currently reading the chunk-data.     */    static final int STATE_READING_CHUNK	    = 2;    /**     * Indicates that a chunk has been completely read and the next      * fields to be examine should be CRLF     */    static final int STATE_AWAITING_CHUNK_EOL	    = 3;    /**     * Indicates that all chunks have been read and the next field     * should be optional trailers or an indication that the chunked     * stream is complete.     */    static final int STATE_AWAITING_TRAILERS	    = 4;    /**     * State to indicate that the chunked stream is complete and     * no further bytes should be read from the underlying stream.     */    static final int STATE_DONE			    = 5;    /**     * Indicates the current state.     */    private int state;    /**     * Check to make sure that this stream has not been closed.     */    private void ensureOpen() throws IOException {	if (closed) {	    throw new IOException("stream is closed");	}    }    /**     * Ensures there is <code>size</code> bytes available in     * <code>rawData<code>. This requires that we either      * shift the bytes in use to the begining of the buffer     * or allocate a large buffer with sufficient space available.     */    private void ensureRawAvailable(int size) {	if (rawCount + size > rawData.length) {	    int used = rawCount - rawPos;		   	    if (used + size > rawData.length) {		byte tmp[] = new byte[used + size];		if (used > 0) {		    System.arraycopy(rawData, rawPos, tmp, 0, used);		}		rawData = tmp;	    } else {		if (used > 0) {		    System.arraycopy(rawData, rawPos, rawData, 0, used);		}	    } 	    rawCount = used;	    rawPos = 0;	}    }    /**     * Close the underlying input stream by either returning it to the     * keep alive cache or closing the stream.     * <p>     * As a chunked stream is inheritly persistent (see HTTP 1.1 RFC) the     * underlying stream can be returned to the keep alive cache if the     * stream can be completely read without error.     */    private void closeUnderlying() throws IOException {	if (in == null) {	    return;	}	if (!error && state == STATE_DONE) {	    hc.finished();	} else {	    if (!hurry()) {		hc.closeServer();	    }	}	in = null;    }    /**     * Attempt to read the remainder of a chunk directly into the     * caller's buffer.     * <p>     * Return the number of bytes read.     */    private int fastRead(byte[] b, int off, int len) throws IOException {	// assert state == STATE_READING_CHUNKS;	int remaining = chunkSize - chunkRead;	int cnt = (remaining < len) ? remaining : len;	if (cnt > 0) {	    int nread;	    try {		nread = in.read(b, off, cnt);	    } catch (IOException e) {		error = true;		throw e;	    }	    if (nread > 0) {		chunkRead += nread;		if (chunkRead >= chunkSize) {		    state = STATE_AWAITING_CHUNK_EOL;		}		return nread;	    }	    error = true;	    throw new IOException("Premature EOF");	} else {	    return 0;	}    }    /**     * Process any outstanding bytes that have already been read into      * <code>rawData</code>.     * <p>     * The parsing of the chunked stream is performed as a state machine with     * <code>state</code> representing the current state of the processing.     * <p>     * Returns when either all the outstanding bytes in rawData have been     * processed or there is insufficient bytes available to continue     * processing. When the latter occurs <code>rawPos</code> will not have     * been updated and thus the processing can be restarted once further     * bytes have been read into <code>rawData</code>.     */    private void processRaw() throws IOException {	int pos;	int i;	while (state != STATE_DONE) {	    switch (state) {		/**		 * We are awaiting a line with a chunk header 		 */		case STATE_AWAITING_CHUNK_HEADER:		    /*		     * Find \n to indicate end of chunk header. If not found when there is		     * insufficient bytes in the raw buffer to parse a chunk header.		     */		    pos = rawPos;		    while (pos < rawCount) {			if (rawData[pos] == '\n') {			    break;			}			pos++;		    }		    if (pos >= rawCount) {			return;		    }		    /*		     * Extract the chunk size from the header (ignoring extensions).		     */		    String header = new String(rawData, rawPos, pos-rawPos+1);		    for (i=0; i < header.length(); i++) {			if (Character.digit(header.charAt(i), 16) == -1)			    break;		    }		    try {			chunkSize = Integer.parseInt(header.substring(0, i), 16);		    } catch (NumberFormatException e) {			error = true;			throw new IOException("Bogus chunk size");		    }		    		    /*		     * Chunk has been parsed so move rawPos to first byte of chunk		     * data.		     */		    rawPos = pos + 1;		    chunkRead = 0;		    			    /*		     * A chunk size of 0 means EOF.		     */		    if (chunkSize > 0) {						state = STATE_READING_CHUNK;		    } else {			state = STATE_AWAITING_TRAILERS;		    }		    break;		/**		 * We are awaiting raw entity data (some may have already been		 * read). chunkSize is the size of the chunk; chunkRead is the		 * total read from the underlying stream to date.		 */		case STATE_READING_CHUNK :		    /* no data available yet */		    if (rawPos >= rawCount) {			return;		    }		    /*		     * Compute the number of bytes of chunk data available in the		     * raw buffer.		     */		    int copyLen = Math.min( chunkSize-chunkRead, rawCount-rawPos );		    /*		     * Expand or compact chunkData if needed.		     */		    if (chunkData.length < chunkCount + copyLen) {			int cnt = chunkCount - chunkPos;			if (chunkData.length < cnt + copyLen) {			    byte tmp[] = new byte[cnt + copyLen];			    System.arraycopy(chunkData, chunkPos, tmp, 0, cnt);			    chunkData = tmp;			} else {			    System.arraycopy(chunkData, chunkPos, chunkData, 0, cnt);			}			chunkPos = 0;			chunkCount = cnt;		    }		    /*		     * Copy the chunk data into chunkData so that it's available		     * to the read methods.		     */		    System.arraycopy(rawData, rawPos, chunkData, chunkCount, copyLen);		    rawPos += copyLen;		    chunkCount += copyLen;		    chunkRead += copyLen;		    /*		     * If all the chunk has been copied into chunkData then the next		     * token should be CRLF. 		     */		    if (chunkSize - chunkRead <= 0) {			state = STATE_AWAITING_CHUNK_EOL;		    } else {			return;		    }		    break;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?