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 */
027public abstract class LimitedInputStream extends FilterInputStream implements Closeable {
028
029    /**
030     * The maximum size of an item, in bytes.
031     */
032    private final long sizeMax;
033
034    /**
035     * The current number of bytes.
036     */
037    private long count;
038
039    /**
040     * Whether this stream is already closed.
041     */
042    private boolean closed;
043
044    /**
045     * Creates a new instance.
046     *
047     * @param inputStream The input stream, which shall be limited.
048     * @param pSizeMax The limit; no more than this number of bytes
049     *   shall be returned by the source stream.
050     */
051    public LimitedInputStream(InputStream inputStream, long pSizeMax) {
052        super(inputStream);
053        sizeMax = pSizeMax;
054    }
055
056    /**
057     * Called to indicate, that the input streams limit has
058     * been exceeded.
059     *
060     * @param pSizeMax The input streams limit, in bytes.
061     * @param pCount The actual number of bytes.
062     * @throws IOException The called method is expected
063     *   to raise an IOException.
064     */
065    protected abstract void raiseError(long pSizeMax, long pCount)
066            throws IOException;
067
068    /**
069     * Called to check, whether the input streams
070     * limit is reached.
071     *
072     * @throws IOException The given limit is exceeded.
073     */
074    private void checkLimit() throws IOException {
075        if (count > sizeMax) {
076            raiseError(sizeMax, count);
077        }
078    }
079
080    /**
081     * Reads the next byte of data from this input stream. The value
082     * byte is returned as an <code>int</code> in the range
083     * <code>0</code> to <code>255</code>. If no byte is available
084     * because the end of the stream has been reached, the value
085     * <code>-1</code> is returned. This method blocks until input data
086     * is available, the end of the stream is detected, or an exception
087     * is thrown.
088     * <p>
089     * This method
090     * simply performs <code>in.read()</code> and returns the result.
091     *
092     * @return     the next byte of data, or <code>-1</code> if the end of the
093     *             stream is reached.
094     * @throws  IOException  if an I/O error occurs.
095     * @see        java.io.FilterInputStream#in
096     */
097    @Override
098    public int read() throws IOException {
099        int res = super.read();
100        if (res != -1) {
101            count++;
102            checkLimit();
103        }
104        return res;
105    }
106
107    /**
108     * Reads up to <code>len</code> bytes of data from this input stream
109     * into an array of bytes. If <code>len</code> is not zero, the method
110     * blocks until some input is available; otherwise, no
111     * bytes are read and <code>0</code> is returned.
112     * <p>
113     * This method simply performs <code>in.read(b, off, len)</code>
114     * and returns the result.
115     *
116     * @param      b     the buffer into which the data is read.
117     * @param      off   The start offset in the destination array
118     *                   <code>b</code>.
119     * @param      len   the maximum number of bytes read.
120     * @return     the total number of bytes read into the buffer, or
121     *             <code>-1</code> if there is no more data because the end of
122     *             the stream has been reached.
123     * @throws  NullPointerException If <code>b</code> is <code>null</code>.
124     * @throws  IndexOutOfBoundsException If <code>off</code> is negative,
125     * <code>len</code> is negative, or <code>len</code> is greater than
126     * <code>b.length - off</code>
127     * @throws  IOException  if an I/O error occurs.
128     * @see        java.io.FilterInputStream#in
129     */
130    @Override
131    public int read(byte[] b, int off, int len) throws IOException {
132        int res = super.read(b, off, len);
133        if (res > 0) {
134            count += res;
135            checkLimit();
136        }
137        return res;
138    }
139
140    /**
141     * Returns, whether this stream is already closed.
142     *
143     * @return True, if the stream is closed, otherwise false.
144     * @throws IOException An I/O error occurred.
145     */
146    @Override
147    public boolean isClosed() throws IOException {
148        return closed;
149    }
150
151    /**
152     * Closes this input stream and releases any system resources
153     * associated with the stream.
154     * This
155     * method simply performs <code>in.close()</code>.
156     *
157     * @throws  IOException  if an I/O error occurs.
158     * @see        java.io.FilterInputStream#in
159     */
160    @Override
161    public void close() throws IOException {
162        closed = true;
163        super.close();
164    }
165
166}