package net.i2p.router.transport;
/*
* free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain
* with no warranty of any kind, either expressed or implied.
* It probably won't make your computer catch on fire, or eat
* your children, but it might. Use at your own risk.
*
*/
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import net.i2p.data.router.RouterIdentity;
import net.i2p.router.RouterContext;
import net.i2p.util.Log;
public class BandwidthLimitedInputStream extends FilterInputStream {
private Log _log;
private RouterIdentity _peer;
private String _peerSource;
private RouterContext _context;
private boolean _pullFromOutbound;
private FIFOBandwidthLimiter.Request _currentRequest;
public BandwidthLimitedInputStream(RouterContext context, InputStream source, RouterIdentity peer) {
this(context, source, peer, false);
}
/**
* @param pullFromOutbound even though this is an input stream, if this is true, use the
* context's outbound bandwidth limiter queue for delays
*/
public BandwidthLimitedInputStream(RouterContext context, InputStream source, RouterIdentity peer, boolean pullFromOutbound) {
super(source);
_context = context;
_peer = peer;
if (peer != null)
_peerSource = peer.getHash().toBase64();
_pullFromOutbound = pullFromOutbound;
_log = context.logManager().getLog(BandwidthLimitedInputStream.class);
}
@Override
public int read() throws IOException {
if (_pullFromOutbound)
_currentRequest = _context.bandwidthLimiter().requestOutbound(1, 0, _peerSource);
else
_currentRequest = _context.bandwidthLimiter().requestInbound(1, _peerSource);
// since its only a single byte, we dont need to loop
// or check how much was allocated
_currentRequest.waitForNextAllocation();
synchronized (this) {
_currentRequest = null;
}
return in.read();
}
@Override
public int read(byte dest[]) throws IOException {
return read(dest, 0, dest.length);
}
@Override
public int read(byte dest[], int off, int len) throws IOException {
int read = in.read(dest, off, len);
if (read == -1) return -1;
if (_pullFromOutbound)
_currentRequest = _context.bandwidthLimiter().requestOutbound(read, 0, _peerSource);
else
_currentRequest = _context.bandwidthLimiter().requestInbound(read, _peerSource);
while (_currentRequest.getPendingRequested() > 0) {
// we still haven't been authorized for everything, keep on waiting
_currentRequest.waitForNextAllocation();
if (_currentRequest.getAborted()) {
if (_log.shouldLog(Log.WARN))
_log.warn("Request aborted while trying to read " + len + " (actually read " + read + ")");
break;
}
}
synchronized (this) {
_currentRequest = null;
}
return read;
}
@Override
public long skip(long numBytes) throws IOException {
long skip = in.skip(numBytes);
if (_pullFromOutbound)
_currentRequest = _context.bandwidthLimiter().requestOutbound((int)skip, 0, _peerSource);
else
_currentRequest = _context.bandwidthLimiter().requestInbound((int)skip, _peerSource);
while (_currentRequest.getPendingRequested() > 0) {
// we still haven't been authorized for everything, keep on waiting
_currentRequest.waitForNextAllocation();
if (_currentRequest.getAborted()) {
if (_log.shouldLog(Log.WARN))
_log.warn("Request aborted while trying to skip " + numBytes);
break;
}
}
return skip;
}
@Override
public void close() throws IOException {
synchronized (this) {
if (_currentRequest != null)
_currentRequest.abort();
}
super.close();
}
}