001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.fileupload.util;
018
019import java.io.FilterInputStream;
020import java.io.IOException;
021import java.io.InputStream;
022
023/**
024 * An input stream, which limits its data size. This stream is
025 * used, if the content length is unknown.
026 *
027 * @version $Id: LimitedInputStream.java 1565292 2014-02-06 14:51:59Z ggregory $
028 */
029public abstract class LimitedInputStream extends FilterInputStream implements Closeable {
030
031    /**
032     * The maximum size of an item, in bytes.
033     */
034    private final long sizeMax;
035
036    /**
037     * The current number of bytes.
038     */
039    private long count;
040
041    /**
042     * Whether this stream is already closed.
043     */
044    private boolean closed;
045
046    /**
047     * Creates a new instance.
048     *
049     * @param inputStream The input stream, which shall be limited.
050     * @param pSizeMax The limit; no more than this number of bytes
051     *   shall be returned by the source stream.
052     */
053    public LimitedInputStream(InputStream inputStream, long pSizeMax) {
054        super(inputStream);
055        sizeMax = pSizeMax;
056    }
057
058    /**
059     * Called to indicate, that the input streams limit has
060     * been exceeded.
061     *
062     * @param pSizeMax The input streams limit, in bytes.
063     * @param pCount The actual number of bytes.
064     * @throws IOException The called method is expected
065     *   to raise an IOException.
066     */
067    protected abstract void raiseError(long pSizeMax, long pCount)
068            throws IOException;
069
070    /**
071     * Called to check, whether the input streams
072     * limit is reached.
073     *
074     * @throws IOException The given limit is exceeded.
075     */
076    private void checkLimit() throws IOException {
077        if (count > sizeMax) {
078            raiseError(sizeMax, count);
079        }
080    }
081
082    /**
083     * Reads the next byte of data from this input stream. The value
084     * byte is returned as an <code>int</code> in the range
085     * <code>0</code> to <code>255</code>. If no byte is available
086     * because the end of the stream has been reached, the value
087     * <code>-1</code> is returned. This method blocks until input data
088     * is available, the end of the stream is detected, or an exception
089     * is thrown.
090     * <p>
091     * This method
092     * simply performs <code>in.read()</code> and returns the result.
093     *
094     * @return     the next byte of data, or <code>-1</code> if the end of the
095     *             stream is reached.
096     * @exception  IOException  if an I/O error occurs.
097     * @see        java.io.FilterInputStream#in
098     */
099    @Override
100    public int read() throws IOException {
101        int res = super.read();
102        if (res != -1) {
103            count++;
104            checkLimit();
105        }
106        return res;
107    }
108
109    /**
110     * Reads up to <code>len</code> bytes of data from this input stream
111     * into an array of bytes. If <code>len</code> is not zero, the method
112     * blocks until some input is available; otherwise, no
113     * bytes are read and <code>0</code> is returned.
114     * <p>
115     * This method simply performs <code>in.read(b, off, len)</code>
116     * and returns the result.
117     *
118     * @param      b     the buffer into which the data is read.
119     * @param      off   The start offset in the destination array
120     *                   <code>b</code>.
121     * @param      len   the maximum number of bytes read.
122     * @return     the total number of bytes read into the buffer, or
123     *             <code>-1</code> if there is no more data because the end of
124     *             the stream has been reached.
125     * @exception  NullPointerException If <code>b</code> is <code>null</code>.
126     * @exception  IndexOutOfBoundsException If <code>off</code> is negative,
127     * <code>len</code> is negative, or <code>len</code> is greater than
128     * <code>b.length - off</code>
129     * @exception  IOException  if an I/O error occurs.
130     * @see        java.io.FilterInputStream#in
131     */
132    @Override
133    public int read(byte[] b, int off, int len) throws IOException {
134        int res = super.read(b, off, len);
135        if (res > 0) {
136            count += res;
137            checkLimit();
138        }
139        return res;
140    }
141
142    /**
143     * Returns, whether this stream is already closed.
144     *
145     * @return True, if the stream is closed, otherwise false.
146     * @throws IOException An I/O error occurred.
147     */
148    public boolean isClosed() throws IOException {
149        return closed;
150    }
151
152    /**
153     * Closes this input stream and releases any system resources
154     * associated with the stream.
155     * This
156     * method simply performs <code>in.close()</code>.
157     *
158     * @exception  IOException  if an I/O error occurs.
159     * @see        java.io.FilterInputStream#in
160     */
161    @Override
162    public void close() throws IOException {
163        closed = true;
164        super.close();
165    }
166
167}