package tap.upload; /* * This file is part of TAPLibrary. * * TAPLibrary is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * TAPLibrary is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with TAPLibrary. If not, see <http://www.gnu.org/licenses/>. * * Copyright 2012,2014 - UDS/Centre de DonnĂ©es astronomiques de Strasbourg (CDS), * Astronomisches Rechen Institut (ARI) */ import java.io.IOException; import java.io.InputStream; import java.security.InvalidParameterException; import com.oreilly.servlet.multipart.ExceededSizeException; /** * Let limit the number of bytes that can be read from a given input stream. * * @author Grégory Mantelet (CDS;ARI) * @version 2.0 (08/2014) */ public final class LimitedSizeInputStream extends InputStream { /** Input stream whose the number of bytes that can be read must be limited. */ private final InputStream input; /** Maximum number of bytes that can be read. */ private final long sizeLimit; /** Number of bytes currently read. */ private long counter = 0; /** Indicate whether the byte limit has already been reached. If <i>true</i> no more byte can be read ; * all read(...) function will throw an {@link ExceededSizeException}. */ private boolean exceed = false; /** * Wrap the given input stream so that limiting the number of bytes that can be read. * * @param stream Stream to limit. * @param sizeLimit Maximum number of bytes that can be read. <i>If <=0 an {@link InvalidParameterException} will be thrown.</i> * * @throws NullPointerException If the input stream is missing. */ public LimitedSizeInputStream(final InputStream stream, final long sizeLimit) throws NullPointerException{ if (stream == null) throw new NullPointerException("The given input stream is NULL !"); input = stream; if (sizeLimit <= 0) throw new InvalidParameterException("The size limit must be a positive number of bytes !"); this.sizeLimit = sizeLimit; } /** * Get the input stream wrapped by this instance of {@link LimitedSizeInputStream}. * * @return The wrapped input stream. * @since 2.0 */ public final InputStream getInnerStream(){ return input; } /** * <p>Update the number of bytes currently read and them check whether the limit has been exceeded. * If the limit has been exceeded, an {@link ExceededSizeException} is thrown.</p> * * <p>Besides, the flag {@link #exceed} is set to true in order to forbid the further reading of bytes.</p> * * @param nbReads Number of bytes read. * * @throws ExceededSizeException If, after update, the limit of bytes has been exceeded. */ private void updateCounter(final long nbReads) throws ExceededSizeException{ if (nbReads > 0){ counter += nbReads; if (counter > sizeLimit){ exceed = true; throw new ExceededSizeException("Data read overflow: the limit of " + sizeLimit + " bytes has been reached!"); } } } /** * <p>Tell whether the limit has already been exceeded or not.</p> * * <p><i>Note: * If <i>true</i> is returned, no more read will be allowed, and any attempt to read a byte will throw an {@link ExceededSizeException}. * </i></p> * * @return <i>true</i> if the byte limit has been exceeded, <i>false</i> otherwise. */ public final boolean sizeExceeded(){ return exceed; } @Override public int read() throws IOException{ int read = input.read(); updateCounter(1); return read; } @Override public int read(byte[] b) throws IOException{ int nbRead = input.read(b); updateCounter(nbRead); return nbRead; } @Override public int read(byte[] b, int off, int len) throws IOException{ int nbRead = input.read(b, off, len); updateCounter(nbRead); return nbRead; } @Override public long skip(long n) throws IOException{ long nbSkipped = input.skip(n); updateCounter(nbSkipped); return nbSkipped; } @Override public int available() throws IOException{ return input.available(); } @Override public void close() throws IOException{ input.close(); } @Override public synchronized void mark(int readlimit) throws UnsupportedOperationException{ input.mark(readlimit); } @Override public boolean markSupported(){ return input.markSupported(); } @Override public synchronized void reset() throws IOException, UnsupportedOperationException{ input.reset(); } }