
net.lightbody.bmp.proxy.jetty.http.HttpInputStream Maven / Gradle / Ivy
// ========================================================================
// $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 net.lightbody.bmp.proxy.jetty.http;
import net.lightbody.bmp.proxy.jetty.util.LineInput;
import net.lightbody.bmp.proxy.jetty.util.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
/**
* 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.
*
* @author Greg Wilkins (gregw)
* @version $Id: HttpInputStream.java,v 1.13 2005/08/23 20:02:26 gregwilkins Exp $
* @see net.lightbody.bmp.proxy.jetty.util.LineInput
*/
public class HttpInputStream extends FilterInputStream {
private static final ClosedStream __closedStream = new ClosedStream();
private final Logger log = LoggerFactory.getLogger(HttpInputStream.class);
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.error(e.getMessage(), e);
System.exit(1);
}
this.in = _realIn;
}
/**
*/
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().
*
* @throws 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.
*
* @throws 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");
}
log.trace("resetStream()");
in = _realIn;
if (_deChunker != null) {
_deChunker.resetStream();
}
_chunking = false;
_realIn.setByteLimit(-1);
}
public void close() throws IOException {
in = __closedStream;
}
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();
}
/**
* 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);
}
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;
}
}
}