001: // Copyright (C) 1999-2001 by Jason Hunter <jhunter_AT_acm_DOT_org>.
002: // All rights reserved. Use of this class is limited.
003: // Please see the LICENSE for more information.
004:
005: package com.oreilly.servlet.multipart;
006:
007: import java.io.IOException;
008:
009: import javax.servlet.ServletInputStream;
010:
011: /**
012: * A <code>BufferedServletInputStream</code> wraps a
013: * <code>ServletInputStream</code> in order to provide input buffering and to
014: * avoid calling the the <code>readLine</code> method of the wrapped
015: * <code>ServletInputStream</code>.
016: * <p>
017: * This is necessary because some servlet containers rely on the default
018: * implementation of the <code>readLine</code> method provided by the Servlet
019: * API classes, which is very slow. Tomcat 3.2, Tomcat 3.1, the JSWDK 1.0 web
020: * server and the JSDK2.1 web server are all known to need this class for
021: * performance reasons.
022: * <p>
023: * Also, it may be used to work around a bug in the Servlet API 2.0
024: * implementation of <code>readLine</code> which contains a bug that causes
025: * <code>ArrayIndexOutOfBoundsExceptions</code> under certain conditions.
026: * Apache JServ is known to suffer from this bug.
027: *
028: * @author Geoff Soutter
029: * @version 1.1, 2001/05/21, removed block of commented out code
030: * @version 1.0, 2000/10/27, initial revision
031: */
032: public class BufferedServletInputStream extends ServletInputStream {
033:
034: /** input stream we are filtering */
035: private ServletInputStream in;
036:
037: /** our buffer */
038: private byte[] buf = new byte[64 * 1024]; // 64k
039:
040: /** number of bytes we've read into the buffer */
041: private int count;
042:
043: /** current position in the buffer */
044: private int pos;
045:
046: /**
047: * Creates a <code>BufferedServletInputStream</code> that wraps the provided
048: * <code>ServletInputStream</code>.
049: *
050: * @param in a servlet input stream.
051: */
052: public BufferedServletInputStream(ServletInputStream in) {
053: this .in = in;
054: }
055:
056: /**
057: * Fill up our buffer from the underlying input stream. Users of this
058: * method must ensure that they use all characters in the buffer before
059: * calling this method.
060: *
061: * @exception IOException if an I/O error occurs.
062: */
063: private void fill() throws IOException {
064: int i = in.read(buf, 0, buf.length);
065: if (i > 0) {
066: pos = 0;
067: count = i;
068: }
069: }
070:
071: /**
072: * Implement buffering on top of the <code>readLine</code> method of
073: * the wrapped <code>ServletInputStream</code>.
074: *
075: * @param b an array of bytes into which data is read.
076: * @param off an integer specifying the character at which
077: * this method begins reading.
078: * @param len an integer specifying the maximum number of
079: * bytes to read.
080: * @return an integer specifying the actual number of bytes
081: * read, or -1 if the end of the stream is reached.
082: * @exception IOException if an I/O error occurs.
083: */
084: public int readLine(byte b[], int off, int len) throws IOException {
085: int total = 0;
086: if (len == 0) {
087: return 0;
088: }
089:
090: int avail = count - pos;
091: if (avail <= 0) {
092: fill();
093: avail = count - pos;
094: if (avail <= 0) {
095: return -1;
096: }
097: }
098: int copy = Math.min(len, avail);
099: int eol = findeol(buf, pos, copy);
100: if (eol != -1) {
101: copy = eol;
102: }
103: System.arraycopy(buf, pos, b, off, copy);
104: pos += copy;
105: total += copy;
106:
107: while (total < len && eol == -1) {
108: fill();
109: avail = count - pos;
110: if (avail <= 0) {
111: return total;
112: }
113: copy = Math.min(len - total, avail);
114: eol = findeol(buf, pos, copy);
115: if (eol != -1) {
116: copy = eol;
117: }
118: System.arraycopy(buf, pos, b, off + total, copy);
119: pos += copy;
120: total += copy;
121: }
122: return total;
123: }
124:
125: /**
126: * Attempt to find the '\n' end of line marker as defined in the comment of
127: * the <code>readLine</code> method of <code>ServletInputStream</code>.
128: *
129: * @param b byte array to search.
130: * @param pos position in byte array to search from.
131: * @param len maximum number of bytes to search.
132: *
133: * @return the number of bytes including the \n, or -1 if none found.
134: */
135: private static int findeol(byte b[], int pos, int len) {
136: int end = pos + len;
137: int i = pos;
138: while (i < end) {
139: if (b[i++] == '\n') {
140: return i - pos;
141: }
142: }
143: return -1;
144: }
145:
146: /**
147: * Implement buffering on top of the <code>read</code> method of
148: * the wrapped <code>ServletInputStream</code>.
149: *
150: * @return the next byte of data, or <code>-1</code> if the end of the
151: * stream is reached.
152: * @exception IOException if an I/O error occurs.
153: */
154: public int read() throws IOException {
155: if (count <= pos) {
156: fill();
157: if (count <= pos) {
158: return -1;
159: }
160: }
161: return buf[pos++] & 0xff;
162: }
163:
164: /**
165: * Implement buffering on top of the <code>read</code> method of
166: * the wrapped <code>ServletInputStream</code>.
167: *
168: * @param b the buffer into which the data is read.
169: * @param off the start offset of the data.
170: * @param len the maximum number of bytes read.
171: * @return the total number of bytes read into the buffer, or
172: * <code>-1</code> if there is no more data because the end
173: * of the stream has been reached.
174: * @exception IOException if an I/O error occurs.
175: */
176: public int read(byte b[], int off, int len) throws IOException {
177: int total = 0;
178: while (total < len) {
179: int avail = count - pos;
180: if (avail <= 0) {
181: fill();
182: avail = count - pos;
183: if (avail <= 0) {
184: if (total > 0)
185: return total;
186: else
187: return -1;
188: }
189: }
190: int copy = Math.min(len - total, avail);
191: System.arraycopy(buf, pos, b, off + total, copy);
192: pos += copy;
193: total += copy;
194: }
195: return total;
196: }
197: }
|