/* * Copyright 2001-2004 The Apache Software Foundation. * * 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.apache.axis.transport.http; import java.io.IOException; import java.io.InputStream; public class NonBlockingBufferedInputStream extends InputStream { // current stream to be processed private InputStream in; // maximum number of bytes allowed to be returned. private int remainingContent = Integer.MAX_VALUE; // Internal buffer for the input stream private byte[] buffer = new byte[4096]; private int offset = 0; // bytes before this offset have been processed private int numbytes = 0; // number of valid bytes in this buffer /** * set the input stream to be used for subsequent reads * @param in the InputStream */ public void setInputStream (InputStream in) { this.in = in; numbytes = 0; offset = 0; remainingContent = (in==null)? 0 : Integer.MAX_VALUE; } /** * set the maximum number of bytes allowed to be read from this input * stream. * @param value the Content Length */ public void setContentLength (int value) { if (in != null) this.remainingContent = value - (numbytes-offset); } /** * Replenish the buffer with data from the input stream. This is * guaranteed to read atleast one byte or throw an exception. When * possible, it will read up to the length of the buffer * the data is buffered for efficiency. * @return the byte read */ private void refillBuffer() throws IOException { if (remainingContent <= 0 || in == null) return; // determine number of bytes to read numbytes = in.available(); if (numbytes > remainingContent) numbytes=remainingContent; if (numbytes > buffer.length) numbytes=buffer.length; if (numbytes <= 0) numbytes = 1; // actually attempt to read those bytes numbytes = in.read(buffer, 0, numbytes); // update internal state to reflect this read remainingContent -= numbytes; offset = 0; } /** * Read a byte from the input stream, blocking if necessary. Internally * the data is buffered for efficiency. * @return the byte read */ public int read() throws IOException { if (in == null) return -1; if (offset >= numbytes) refillBuffer(); if (offset >= numbytes) return -1; return buffer[offset++] & 0xFF; } /** * Read bytes from the input stream. This is guaranteed to return at * least one byte or throw an exception. When possible, it will return * more bytes, up to the length of the array, as long as doing so would * not require waiting on bytes from the input stream. * @param dest byte array to read into * @return the number of bytes actually read */ public int read(byte[] dest) throws IOException { return read(dest, 0, dest.length); } /** * Read a specified number of bytes from the input stream. This is * guaranteed to return at least one byte or throw an execption. When * possible, it will return more bytes, up to the length specified, * as long as doing so would not require waiting on bytes from the * input stream. * @param dest byte array to read into * @param off starting offset into the byte array * @param len maximum number of bytes to read * @return the number of bytes actually read */ public int read(byte[] dest, int off, int len) throws IOException { int ready = numbytes - offset; if (ready >= len) { System.arraycopy(buffer, offset, dest, off, len); offset += len; return len; } else if (ready>0) { System.arraycopy(buffer, offset, dest, off, ready); offset = numbytes; return ready; } else { if (in == null) return -1; refillBuffer(); if (offset >= numbytes) return -1; return read(dest,off,len); } } /** * skip over (and discard) a specified number of bytes in this input * stream * @param len the number of bytes to be skipped * @return the action number of bytes skipped */ public int skip(int len) throws IOException { int count = 0; while (len-->0 && read()>=0) count++; return count; } /** * return the number of bytes available to be read without blocking * @return the number of bytes */ public int available() throws IOException { if (in == null) return 0; // return buffered + available from the stream return (numbytes-offset) + in.available(); } /** * disassociate from the underlying input stream */ public void close() throws IOException { setInputStream(null); } /** * Just like read except byte is not removed from the buffer. * the data is buffered for efficiency. * Was added to support multiline http headers. ;-) * @return the byte read */ public int peek() throws IOException { if (in == null) return -1; if (offset >= numbytes) refillBuffer(); if (offset >= numbytes) return -1; return buffer[offset] & 0xFF; } }