View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.fileupload.util;
18  
19  import java.io.FilterInputStream;
20  import java.io.IOException;
21  import java.io.InputStream;
22  
23  /**
24   * An input stream, which limits its data size. This stream is
25   * used, if the content length is unknown.
26   */
27  public abstract class LimitedInputStream extends FilterInputStream implements Closeable {
28  
29      /**
30       * The maximum size of an item, in bytes.
31       */
32      private final long sizeMax;
33  
34      /**
35       * The current number of bytes.
36       */
37      private long count;
38  
39      /**
40       * Whether this stream is already closed.
41       */
42      private boolean closed;
43  
44      /**
45       * Creates a new instance.
46       *
47       * @param inputStream The input stream, which shall be limited.
48       * @param sizeMax The limit; no more than this number of bytes
49       *   shall be returned by the source stream.
50       */
51      public LimitedInputStream(final InputStream inputStream, final long sizeMax) {
52          super(inputStream);
53          this.sizeMax = sizeMax;
54      }
55  
56      /**
57       * Called to check, whether the input streams
58       * limit is reached.
59       *
60       * @throws IOException The given limit is exceeded.
61       */
62      private void checkLimit() throws IOException {
63          if (count > sizeMax) {
64              raiseError(sizeMax, count);
65          }
66      }
67  
68      /**
69       * Closes this input stream and releases any system resources
70       * associated with the stream.
71       * This
72       * method simply performs {@code in.close()}.
73       *
74       * @throws  IOException  if an I/O error occurs.
75       * @see        java.io.FilterInputStream#in
76       */
77      @Override
78      public void close() throws IOException {
79          closed = true;
80          super.close();
81      }
82  
83      /**
84       * Returns, whether this stream is already closed.
85       *
86       * @return True, if the stream is closed, otherwise false.
87       * @throws IOException An I/O error occurred.
88       */
89      @Override
90      public boolean isClosed() throws IOException {
91          return closed;
92      }
93  
94      /**
95       * Called to indicate, that the input streams limit has
96       * been exceeded.
97       *
98       * @param sizeMax The input streams limit, in bytes.
99       * @param count The actual number of bytes.
100      * @throws IOException The called method is expected
101      *   to raise an IOException.
102      */
103     protected abstract void raiseError(long sizeMax, long count) throws IOException;
104 
105     /**
106      * Reads the next byte of data from this input stream. The value
107      * byte is returned as an {@code int} in the range
108      * {@code 0} to {@code 255}. If no byte is available
109      * because the end of the stream has been reached, the value
110      * {@code -1} is returned. This method blocks until input data
111      * is available, the end of the stream is detected, or an exception
112      * is thrown.
113      * <p>
114      * This method
115      * simply performs {@code in.read()} and returns the result.
116      * </p>
117      *
118      * @return     the next byte of data, or {@code -1} if the end of the
119      *             stream is reached.
120      * @throws  IOException  if an I/O error occurs.
121      * @see        java.io.FilterInputStream#in
122      */
123     @Override
124     public int read() throws IOException {
125         final int res = super.read();
126         if (res != -1) {
127             count++;
128             checkLimit();
129         }
130         return res;
131     }
132 
133     /**
134      * Reads up to {@code len} bytes of data from this input stream
135      * into an array of bytes. If {@code len} is not zero, the method
136      * blocks until some input is available; otherwise, no
137      * bytes are read and {@code 0} is returned.
138      * <p>
139      * This method simply performs {@code in.read(b, off, len)}
140      * and returns the result.
141      * </p>
142      *
143      * @param      b     the buffer into which the data is read.
144      * @param      off   The start offset in the destination array
145      *                   {@code b}.
146      * @param      len   the maximum number of bytes read.
147      * @return     the total number of bytes read into the buffer, or
148      *             {@code -1} if there is no more data because the end of
149      *             the stream has been reached.
150      * @throws  NullPointerException If {@code b} is {@code null}.
151      * @throws  IndexOutOfBoundsException If {@code off} is negative,
152      * {@code len} is negative, or {@code len} is greater than
153      * {@code b.length - off}
154      * @throws  IOException  if an I/O error occurs.
155      * @see        java.io.FilterInputStream#in
156      */
157     @Override
158     public int read(final byte[] b, final int off, final int len) throws IOException {
159         final int res = super.read(b, off, len);
160         if (res > 0) {
161             count += res;
162             checkLimit();
163         }
164         return res;
165     }
166 
167 }