blob: 74a33bde928a89ad082aefa6e85244d59e98b58b [file] [log] [blame]
// ***************************************************************************************************************************
// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file *
// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file *
// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance *
// * with the License. You may obtain a copy of the License at *
// * *
// * http://www.apache.org/licenses/LICENSE-2.0 *
// * *
// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an *
// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the *
// * specific language governing permissions and limitations under the License. *
// ***************************************************************************************************************************
package org.apache.juneau.rest.util;
import java.io.*;
import javax.servlet.*;
/**
* ServletInputStream wrapper around a normal input stream with support for limiting input.
*/
public final class BoundedServletInputStream extends ServletInputStream {
private final InputStream is;
private final ServletInputStream sis;
private long remain;
/**
* Wraps the specified input stream.
*
* @param is The input stream to wrap.
* @param max The maximum number of bytes to read from the stream.
*/
public BoundedServletInputStream(InputStream is, long max) {
this.is = is;
this.sis = null;
this.remain = max;
}
/**
* Wraps the specified input stream.
*
* @param sis The input stream to wrap.
* @param max The maximum number of bytes to read from the stream.
*/
public BoundedServletInputStream(ServletInputStream sis, long max) {
this.sis = sis;
this.is = sis;
this.remain = max;
}
/**
* Wraps the specified byte array.
*
* @param b The byte contents of the stream.
*/
public BoundedServletInputStream(byte[] b) {
this(new ByteArrayInputStream(b), Long.MAX_VALUE);
}
@Override /* InputStream */
public final int read() throws IOException {
decrement();
return is.read();
}
@Override /* InputStream */
public int read(byte[] b) throws IOException {
return read(b, 0, b.length);
}
@Override /* InputStream */
public int read(final byte[] b, final int off, final int len) throws IOException {
long numBytes = Math.min(len, remain);
int r = is.read(b, off, (int) numBytes);
if (r == -1)
return -1;
decrement(numBytes);
return r;
}
@Override /* InputStream */
public long skip(final long n) throws IOException {
long toSkip = Math.min(n, remain);
long r = is.skip(toSkip);
decrement(r);
return r;
}
@Override /* InputStream */
public int available() throws IOException {
if (remain <= 0)
return 0;
return is.available();
}
@Override /* InputStream */
public synchronized void reset() throws IOException {
is.reset();
}
@Override /* InputStream */
public synchronized void mark(int limit) {
is.mark(limit);
}
@Override /* InputStream */
public boolean markSupported() {
return is.markSupported();
}
@Override /* InputStream */
public final void close() throws IOException {
is.close();
}
@Override /* ServletInputStream */
public boolean isFinished() {
return sis == null ? false : sis.isFinished();
}
@Override /* ServletInputStream */
public boolean isReady() {
return sis == null ? true : sis.isReady();
}
@Override /* ServletInputStream */
public void setReadListener(ReadListener arg0) {
if (sis != null)
sis.setReadListener(arg0);
}
private void decrement() throws IOException {
remain--;
if (remain < 0)
throw new IOException("Input limit exceeded. See @Rest(maxInput).");
}
private void decrement(long count) throws IOException {
remain -= count;
if (remain < 0)
throw new IOException("Input limit exceeded. See @Rest(maxInput).");
}
}