HTTPClient.BufferedInputStream Maven / Gradle / Ivy
Show all versions of grinder-httpclient Show documentation
/*
* @(#)BufferedInputStream.java 0.3-3 06/05/2001
*
* This file is part of the HTTPClient package
* Copyright (C) 1996-2001 Ronald Tschalär
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307, USA
*
* For questions, suggestions, bug-reports, enhancement-requests etc.
* I may be contacted at:
*
* [email protected]
*
* The HTTPClient's home page is located at:
*
* http://www.innovation.ch/java/HTTPClient/
*
*/
package HTTPClient;
import java.io.InputStream;
import java.io.FilterInputStream;
import java.io.IOException;
/**
* This class is similar to java.io.BufferedInputStream, except that it fixes
* certain bugs and provides support for finding multipart boundaries.
*
* Note: none of the methods here are synchronized because we assume the
* caller is already taking care of that.
*
* @version 0.3-3 06/05/2001
* @author Ronald Tschalär
*/
class BufferedInputStream extends FilterInputStream
{
/** our read buffer */
private byte[] buffer = new byte[2000];
/** the next byte in the buffer at which to read */
private int pos = 0;
/** the end of the valid data in the buffer */
private int end = 0;
/** the current mark position, or -1 if none */
private int mark_pos = -1;
/**
* the large read threashhold: reads larger than this aren't buffered if
* both the current buffer is empty and no mark has been set. This is just
* an attempt to balance copying vs. multiple reads.
*/
private int lr_thrshld = 1500;
/**
* Create a new BufferedInputStream around the given input stream.
*
* @param stream the underlying input stream to use
*/
BufferedInputStream(InputStream stream)
{
super(stream);
}
/**
* Read a single byte.
*
* @return the read byte, or -1 if the end of the stream has been reached
* @exception IOException if thrown by the underlying stream
*/
public int read() throws IOException
{
if (pos >= end)
fillBuff();
return (end > pos) ? (buffer[pos++] & 0xFF) : -1;
}
/**
* Read a buffer full.
*
* @param buf the buffer to read into
* @param off the offset within buf at which to start writing
* @param len the number of bytes to read
* @return the number of bytes read
* @exception IOException if thrown by the underlying stream
*/
public int read(byte[] buf, int off, int len) throws IOException
{
if (len <= 0)
return 0;
// optimize for large reads
if (pos >= end && len >= lr_thrshld && mark_pos < 0)
return in.read(buf, off, len);
if (pos >= end)
fillBuff();
if (pos >= end)
return -1;
int left = end - pos;
if (len > left)
len = left;
System.arraycopy(buffer, pos, buf, off, len);
pos += len;
return len;
}
/**
* Skip the given number of bytes in the stream.
*
* @param n the number of bytes to skip
* @return the actual number of bytes skipped
* @exception IOException if thrown by the underlying stream
*/
public long skip(long n) throws IOException
{
if (n <= 0)
return 0;
int left = end - pos;
if (n <= left)
{
pos += n;
return n;
}
else
{
pos = end;
return left + in.skip(n - left);
}
}
/**
* Fill buffer by reading from the underlying stream. This assumes the
* current buffer is empty, i.e. pos == end.
*/
private final void fillBuff() throws IOException
{
if (mark_pos > 0) // keep the marked stuff around if possible
{
// only copy if we don't have any space left
if (end >= buffer.length)
{
System.arraycopy(buffer, mark_pos, buffer, 0, end - mark_pos);
pos = end - mark_pos;
}
}
else if (mark_pos == 0 && end < buffer.length)
; // pos == end, so we just fill what's left
else
pos = 0; // try to fill complete buffer
// make sure our state is consistent even if read() throws InterruptedIOException
end = pos;
int got = in.read(buffer, pos, buffer.length - pos);
if (got > 0)
end = pos + got;
}
/**
* @return the number of bytes available for reading without blocking
* @exception IOException if the buffer is empty and the underlying stream has been
* closed
*/
public int available() throws IOException
{
int avail = end - pos;
if (avail == 0)
return in.available();
try
{ avail += in.available(); }
catch (IOException ignored)
{ /* ignore this because we have something available */ }
return avail;
}
/**
* Mark the current read position so that we can start searching for the end boundary.
*/
void markForSearch()
{
mark_pos = pos;
}
/**
* Figures out how many bytes past the end of the multipart we read. If we
* found the end, it then resets the read pos to just past the end of the
* boundary and unsets the mark; if not found, is sets the mark_pos back
* enough from the current position so we can always be sure to find the
* boundary.
*
* @param search the search string (end boundary)
* @param search_cmp the compiled info of the search string
* @return how many bytes past the end of the boundary we went; -1 if we
* haven't gone passed it yet.
*/
int pastEnd(byte[] search, int[] search_cmp)
{
int idx = Util.findStr(search, search_cmp, buffer, mark_pos, pos);
if (idx == -1)
mark_pos = (pos > search.length) ? pos - search.length : 0;
else
{
int eos = idx + search.length;
idx = pos - eos;
pos = eos;
mark_pos = -1;
}
return idx;
}
}