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 sizeMax The limit; no more than this number of bytes 049 * shall be returned by the source stream. 050 */ 051 public LimitedInputStream(final InputStream inputStream, final long sizeMax) { 052 super(inputStream); 053 this.sizeMax = sizeMax; 054 } 055 056 /** 057 * Called to check, whether the input streams 058 * limit is reached. 059 * 060 * @throws IOException The given limit is exceeded. 061 */ 062 private void checkLimit() throws IOException { 063 if (count > sizeMax) { 064 raiseError(sizeMax, count); 065 } 066 } 067 068 /** 069 * Closes this input stream and releases any system resources 070 * associated with the stream. 071 * This 072 * method simply performs {@code in.close()}. 073 * 074 * @throws IOException if an I/O error occurs. 075 * @see java.io.FilterInputStream#in 076 */ 077 @Override 078 public void close() throws IOException { 079 closed = true; 080 super.close(); 081 } 082 083 /** 084 * Returns, whether this stream is already closed. 085 * 086 * @return True, if the stream is closed, otherwise false. 087 * @throws IOException An I/O error occurred. 088 */ 089 @Override 090 public boolean isClosed() throws IOException { 091 return closed; 092 } 093 094 /** 095 * Called to indicate, that the input streams limit has 096 * been exceeded. 097 * 098 * @param sizeMax The input streams limit, in bytes. 099 * @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}