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(); } }