Java tutorial
// ======================================================================== // $Id: HttpInputStream.java,v 1.13 2005/08/23 20:02:26 gregwilkins Exp $ // Copyright 1996-2004 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // http://www.apache.org/licenses/LICENSE-2.0 // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // ======================================================================== package org.openqa.jetty.http; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import org.apache.commons.logging.Log; import org.openqa.jetty.log.LogFactory; import org.openqa.jetty.util.LineInput; import org.openqa.jetty.util.StringUtil; /* ------------------------------------------------------------ */ /** HTTP Chunking InputStream. * This FilterInputStream acts as a BufferedInputStream until * setChunking(true) is called. Once chunking is * enabled, the raw stream is chunk decoded as per RFC2616. * * The "8859-1" encoding is used on underlying LineInput instance for * line based reads from the raw stream. * * This class is not synchronized and should be synchronized * explicitly if an instance is used by multiple threads. * * @see org.openqa.jetty.util.LineInput * @version $Id: HttpInputStream.java,v 1.13 2005/08/23 20:02:26 gregwilkins Exp $ * @author Greg Wilkins (gregw) */ public class HttpInputStream extends FilterInputStream { private static Log log = LogFactory.getLog(HttpInputStream.class); /* ------------------------------------------------------------ */ private static ClosedStream __closedStream = new ClosedStream(); /* ------------------------------------------------------------ */ private ChunkingInputStream _deChunker; private LineInput _realIn; private boolean _chunking; private OutputStream _expectContinues; /* ------------------------------------------------------------ */ /** Constructor. */ public HttpInputStream(InputStream in) { this(in, 4096); } /* ------------------------------------------------------------ */ /** Constructor. */ public HttpInputStream(InputStream in, int bufferSize) { super(null); try { _realIn = new LineInput(in, bufferSize, StringUtil.__ISO_8859_1); } catch (UnsupportedEncodingException e) { log.fatal(e); System.exit(1); } this.in = _realIn; } /* ------------------------------------------------------------ */ /** * @return OutputStream object set by setExpectContinues() * @see #expectContinues() */ public OutputStream getExpectContinues() { return _expectContinues; } /* ------------------------------------------------------------ */ /** * @param expectContinues The expectContinues to set. */ public void setExpectContinues(OutputStream expectContinues) { _expectContinues = expectContinues; } /* ------------------------------------------------------------ */ /* * @see java.io.InputStream#read() */ public int read() throws IOException { if (_expectContinues != null) expectContinues(); return super.read(); } /* ------------------------------------------------------------ */ /* * @see java.io.InputStream#read(byte[], int, int) */ public int read(byte[] b, int off, int len) throws IOException { if (_expectContinues != null) expectContinues(); return super.read(b, off, len); } /* ------------------------------------------------------------ */ /* * @see java.io.InputStream#read(byte[]) */ public int read(byte[] b) throws IOException { if (_expectContinues != null) expectContinues(); return super.read(b); } /* ------------------------------------------------------------ */ /* * @see java.io.InputStream#skip(long) */ public long skip(long n) throws IOException { if (_expectContinues != null) expectContinues(); return super.skip(n); } /* ------------------------------------------------------------ */ private void expectContinues() throws IOException { try { if (available() <= 0) { _expectContinues.write(HttpResponse.__Continue); _expectContinues.flush(); } } finally { _expectContinues = null; } } /* ------------------------------------------------------------ */ /** Get the raw stream. * A stream without filters or chunking is returned. This stream * may still be buffered and uprocessed bytes may be in the buffer. * @return Raw InputStream. */ public InputStream getInputStream() { return _realIn; } /* ------------------------------------------------------------ */ /** Get Filter InputStream. * Get the current top of the InputStream filter stack * @return InputStream. */ public InputStream getFilterStream() { return in; } /* ------------------------------------------------------------ */ /** Set Filter InputStream. * Set input filter stream, which should be constructed to wrap * the stream returned from get FilterStream. */ public void setFilterStream(InputStream filter) { in = filter; } /* ------------------------------------------------------------ */ /** Get chunking mode */ public boolean isChunking() { return _chunking; } /* ------------------------------------------------------------ */ /** Set chunking mode. * Chunking can only be turned off with a call to resetStream(). * @exception IllegalStateException Checking cannot be set if * a content length has been set. */ public void setChunking() throws IllegalStateException { if (_realIn.getByteLimit() >= 0) throw new IllegalStateException("Has Content-Length"); if (_deChunker == null) _deChunker = new ChunkingInputStream(_realIn); in = _deChunker; _chunking = true; _deChunker._trailer = null; } /* ------------------------------------------------------------ */ /** Reset the stream. * Turn chunking off and disable all filters. * @exception IllegalStateException The stream cannot be reset if * there is some unread chunked input or a content length greater * than zero remaining. */ public void resetStream() throws IllegalStateException { if ((_deChunker != null && _deChunker._chunkSize > 0) || _realIn.getByteLimit() > 0) throw new IllegalStateException("Unread input"); if (log.isTraceEnabled()) log.trace("resetStream()"); in = _realIn; if (_deChunker != null) _deChunker.resetStream(); _chunking = false; _realIn.setByteLimit(-1); } /* ------------------------------------------------------------ */ public void close() throws IOException { in = __closedStream; } /* ------------------------------------------------------------ */ /** Set the content length. * Only this number of bytes can be read before EOF is returned. * @param len length. */ public void setContentLength(int len) { if (_chunking && len >= 0 && getExpectContinues() == null) throw new IllegalStateException("Chunking"); _realIn.setByteLimit(len); } /* ------------------------------------------------------------ */ void unsafeSetContentLength(int len) { _realIn.setByteLimit(len); } /* ------------------------------------------------------------ */ /** Get the content length. * @return Number of bytes until EOF is returned or -1 for no limit. */ public int getContentLength() { return _realIn.getByteLimit(); } /* ------------------------------------------------------------ */ public HttpFields getTrailer() { return _deChunker._trailer; } /* ------------------------------------------------------------ */ public void destroy() { if (_realIn != null) _realIn.destroy(); _realIn = null; _deChunker = null; _expectContinues = null; } /* ------------------------------------------------------------ */ /** A closed input stream. */ private static class ClosedStream extends InputStream { /* ------------------------------------------------------------ */ public int read() throws IOException { return -1; } } }