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 }