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.FilterOutputStream; import java.io.IOException; import java.io.OutputStream; import net.i2p.data.router.RouterIdentity; import net.i2p.router.RouterContext; import net.i2p.util.Log; public class BandwidthLimitedOutputStream extends FilterOutputStream { private RouterIdentity _peer; private String _peerTarget; private RouterContext _context; private Log _log; private FIFOBandwidthLimiter.Request _currentRequest; public BandwidthLimitedOutputStream(RouterContext context, OutputStream source, RouterIdentity peer) { super(source); _context = context; _peer = peer; if (peer != null) _peerTarget = peer.getHash().toBase64(); else _peerTarget = "unknown"; _log = context.logManager().getLog(BandwidthLimitedOutputStream.class); _currentRequest = null; } public FIFOBandwidthLimiter.Request getCurrentRequest() { return _currentRequest; } @Override public void write(int val) throws IOException { if (_log.shouldLog(Log.DEBUG)) _log.debug("Writing a single byte!", new Exception("Single byte from...")); long before = _context.clock().now(); FIFOBandwidthLimiter.Request req = _context.bandwidthLimiter().requestOutbound(1, 0, _peerTarget); // only a single byte, no need to loop req.waitForNextAllocation(); long waited = _context.clock().now() - before; if ( (waited > 1000) && (_log.shouldLog(Log.WARN)) ) _log.warn("Waiting to write a byte took too long [" + waited + "ms"); out.write(val); } @Override public void write(byte src[]) throws IOException { write(src, 0, src.length); } @Override public void write(byte src[], int off, int len) throws IOException { if (_log.shouldLog(Log.DEBUG)) _log.debug("Writing " + len + " bytes"); if (src == null) return; if (len <= 0) return; if (len + off > src.length) throw new IllegalArgumentException("what are you thinking? len=" + len + ", off=" + off + ", data=" + src.length); _currentRequest = _context.bandwidthLimiter().requestOutbound(len, 0, _peerTarget); int written = 0; while (written < len) { int allocated = len - _currentRequest.getPendingRequested(); int toWrite = allocated - written; if (toWrite > 0) { try { out.write(src, off + written, toWrite); } catch (IOException ioe) { _currentRequest.abort(); _currentRequest = null; throw ioe; } written += toWrite; } _currentRequest.waitForNextAllocation(); } synchronized (this) { _currentRequest = null; } } @Override public void close() throws IOException { synchronized (this) { if (_currentRequest != null) _currentRequest.abort(); } super.close(); } }