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 }