From 5eda7c30fcfbe1d6740712adfb8bddf44d621fc6 Mon Sep 17 00:00:00 2001 From: zzz <zzz@mail.i2p> Date: Tue, 10 Mar 2009 06:09:50 +0000 Subject: [PATCH] bw limits msg --- .../i2p/client/BWLimitsMessageHandler.java | 25 +++++ .../src/net/i2p/client/I2PSimpleSession.java | 38 ++++++- .../i2p/data/i2cp/BandwidthLimitsMessage.java | 101 ++++++++++++++++++ .../data/i2cp/GetBandwidthLimitsMessage.java | 56 ++++++++++ .../net/i2p/data/i2cp/I2CPMessageHandler.java | 4 + .../client/ClientMessageEventListener.java | 23 ++++ 6 files changed, 244 insertions(+), 3 deletions(-) create mode 100644 core/java/src/net/i2p/client/BWLimitsMessageHandler.java create mode 100644 core/java/src/net/i2p/data/i2cp/BandwidthLimitsMessage.java create mode 100644 core/java/src/net/i2p/data/i2cp/GetBandwidthLimitsMessage.java diff --git a/core/java/src/net/i2p/client/BWLimitsMessageHandler.java b/core/java/src/net/i2p/client/BWLimitsMessageHandler.java new file mode 100644 index 0000000000..b47eaa6c8f --- /dev/null +++ b/core/java/src/net/i2p/client/BWLimitsMessageHandler.java @@ -0,0 +1,25 @@ +package net.i2p.client; + +/* + * Released into the public domain + * with no warranty of any kind, either expressed or implied. + */ + +import net.i2p.I2PAppContext; +import net.i2p.data.i2cp.I2CPMessage; +import net.i2p.data.i2cp.BandwidthLimitsMessage; + +/** + * Handle I2CP BW replies from the router + */ +class BWLimitsMessageHandler extends HandlerImpl { + public BWLimitsMessageHandler(I2PAppContext ctx) { + super(ctx, BandwidthLimitsMessage.MESSAGE_TYPE); + } + + public void handleMessage(I2CPMessage message, I2PSessionImpl session) { + _log.debug("Handle message " + message); + BandwidthLimitsMessage msg = (BandwidthLimitsMessage) message; + ((I2PSimpleSession)session).bwReceived(msg.getLimits()); + } +} diff --git a/core/java/src/net/i2p/client/I2PSimpleSession.java b/core/java/src/net/i2p/client/I2PSimpleSession.java index fcfafe7672..ae588e05f1 100644 --- a/core/java/src/net/i2p/client/I2PSimpleSession.java +++ b/core/java/src/net/i2p/client/I2PSimpleSession.java @@ -17,25 +17,32 @@ import net.i2p.I2PAppContext; import net.i2p.data.DataHelper; import net.i2p.data.Destination; import net.i2p.data.Hash; +import net.i2p.data.i2cp.BandwidthLimitsMessage; import net.i2p.data.i2cp.DestLookupMessage; import net.i2p.data.i2cp.DestReplyMessage; +import net.i2p.data.i2cp.GetBandwidthLimitsMessage; import net.i2p.data.i2cp.I2CPMessageReader; import net.i2p.util.I2PThread; import net.i2p.util.Log; /** - * Create a new session for doing naming queries only. Do not create a Destination. + * Create a new session for doing naming and bandwidth queries only. Do not create a Destination. * Don't create a producer. Do not send/receive messages to other Destinations. * Cannot handle multiple simultaneous queries atm. * Could be expanded to ask the router other things. + * + * @author zzz */ class I2PSimpleSession extends I2PSessionImpl2 { private boolean _destReceived; private Object _destReceivedLock; private Destination _destination; + private boolean _bwReceived; + private Object _bwReceivedLock; + private int[] _bwLimits; /** - * Create a new session for doing naming queries only. Do not create a destination. + * Create a new session for doing naming and bandwidth queries only. Do not create a destination. * * @throws I2PSessionException if there is a problem */ @@ -94,6 +101,14 @@ class I2PSimpleSession extends I2PSessionImpl2 { } } + void bwReceived(int[] i) { + _bwReceived = true; + _bwLimits = i; + synchronized (_bwReceivedLock) { + _bwReceivedLock.notifyAll(); + } + } + public Destination lookupDest(Hash h) throws I2PSessionException { if (_closed) return null; @@ -110,14 +125,31 @@ class I2PSimpleSession extends I2PSessionImpl2 { return _destination; } + public int[] bandwidthLimits() throws I2PSessionException { + if (_closed) + return null; + _bwReceivedLock = new Object(); + sendMessage(new GetBandwidthLimitsMessage()); + for (int i = 0; i < 5 && !_destReceived; i++) { + try { + synchronized (_bwReceivedLock) { + _bwReceivedLock.wait(1000); + } + } catch (InterruptedException ie) {} + } + _bwReceived = false; + return _bwLimits; + } + /** * Only map message handlers that we will use */ class SimpleMessageHandlerMap extends I2PClientMessageHandlerMap { public SimpleMessageHandlerMap(I2PAppContext context) { - int highest = DestReplyMessage.MESSAGE_TYPE; + int highest = Math.max(DestReplyMessage.MESSAGE_TYPE, BandwidthLimitsMessage.MESSAGE_TYPE); _handlers = new I2CPMessageHandler[highest+1]; _handlers[DestReplyMessage.MESSAGE_TYPE] = new DestReplyMessageHandler(context); + _handlers[BandwidthLimitsMessage.MESSAGE_TYPE] = new BWLimitsMessageHandler(context); } } } diff --git a/core/java/src/net/i2p/data/i2cp/BandwidthLimitsMessage.java b/core/java/src/net/i2p/data/i2cp/BandwidthLimitsMessage.java new file mode 100644 index 0000000000..2f8d567869 --- /dev/null +++ b/core/java/src/net/i2p/data/i2cp/BandwidthLimitsMessage.java @@ -0,0 +1,101 @@ +package net.i2p.data.i2cp; + +/* + * public domain + * + */ + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; + +import net.i2p.data.DataFormatException; +import net.i2p.data.DataHelper; +import net.i2p.util.Log; + +/** + * Tell the other side the limits + * + * @author zzz + */ +public class BandwidthLimitsMessage extends I2CPMessageImpl { + private final static Log _log = new Log(BandwidthLimitsMessage.class); + public final static int MESSAGE_TYPE = 23; + private static final int LIMITS = 16; + private int[] data; + + public BandwidthLimitsMessage() { + super(); + data = new int[LIMITS]; + } + + /** + * Let's define it this way. + * Leave some extra. This is only local and rarely sent so we don't care about waste. + * + * 0) Client inbound limit (KBps) + * 1) Client outbound limit (KBps) + * 2) Router inbound limit (KBps) + * 3) Router inbound burst limit (KBps) + * 4) Router outbound limit (KBps) + * 5) Router outbound burst limit (KBps) + * 6) Router burst time (seconds) + * 7-15) undefined + */ + public BandwidthLimitsMessage(int in, int out) { + this(); + data[0] = in; + data[1] = out; + } + + public int[] getLimits() { + return data; + } + + @Override + protected void doReadMessage(InputStream in, int size) throws I2CPMessageException, IOException { + try { + for (int i = 0; i < LIMITS; i++) { + data[i] = (int) DataHelper.readLong(in, 4); + } + } catch (DataFormatException dfe) { + throw new I2CPMessageException("Unable to load the message data", dfe); + } + } + + @Override + protected byte[] doWriteMessage() throws I2CPMessageException, IOException { + ByteArrayOutputStream os = new ByteArrayOutputStream(64); + try { + for (int i = 0; i < LIMITS; i++) { + DataHelper.writeLong(os, 4, data[i]); + } + } catch (DataFormatException dfe) { + throw new I2CPMessageException("Error writing out the message data", dfe); + } + return os.toByteArray(); + } + + public int getType() { + return MESSAGE_TYPE; + } + + @Override + public boolean equals(Object object) { + if ((object != null) && (object instanceof BandwidthLimitsMessage)) { + BandwidthLimitsMessage msg = (BandwidthLimitsMessage) object; + return DataHelper.eq(data, msg.getLimits()); + } + return false; + } + + @Override + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append("[BandwidthLimitsMessage"); + buf.append("\n\tIn: ").append(data[0]); + buf.append("\n\tOut: ").append(data[1]); + buf.append("]"); + return buf.toString(); + } +} diff --git a/core/java/src/net/i2p/data/i2cp/GetBandwidthLimitsMessage.java b/core/java/src/net/i2p/data/i2cp/GetBandwidthLimitsMessage.java new file mode 100644 index 0000000000..a47f883d78 --- /dev/null +++ b/core/java/src/net/i2p/data/i2cp/GetBandwidthLimitsMessage.java @@ -0,0 +1,56 @@ +package net.i2p.data.i2cp; + +/* + * public domain + * + */ + +import java.io.IOException; +import java.io.InputStream; + +import net.i2p.util.Log; + +/** + * Request the router tells us the current bw limits + * + * @author zzz + */ +public class GetBandwidthLimitsMessage extends I2CPMessageImpl { + private final static Log _log = new Log(GetBandwidthLimitsMessage.class); + public final static int MESSAGE_TYPE = 8; + + public GetBandwidthLimitsMessage() { + super(); + } + + @Override + protected void doReadMessage(InputStream in, int size) throws I2CPMessageException, IOException { + // noop + } + + @Override + protected byte[] doWriteMessage() throws I2CPMessageException, IOException { + byte rv[] = new byte[0]; + return rv; + } + + public int getType() { + return MESSAGE_TYPE; + } + + @Override + public boolean equals(Object object) { + if ((object != null) && (object instanceof GetBandwidthLimitsMessage)) { + return true; + } + + return false; + } + + @Override + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append("[GetBandwidthLimitsMessage]"); + return buf.toString(); + } +} diff --git a/core/java/src/net/i2p/data/i2cp/I2CPMessageHandler.java b/core/java/src/net/i2p/data/i2cp/I2CPMessageHandler.java index 61d8650534..04da546f78 100644 --- a/core/java/src/net/i2p/data/i2cp/I2CPMessageHandler.java +++ b/core/java/src/net/i2p/data/i2cp/I2CPMessageHandler.java @@ -94,6 +94,10 @@ public class I2CPMessageHandler { return new DestLookupMessage(); case DestReplyMessage.MESSAGE_TYPE: return new DestReplyMessage(); + case GetBandwidthLimitsMessage.MESSAGE_TYPE: + return new GetBandwidthLimitsMessage(); + case BandwidthLimitsMessage.MESSAGE_TYPE: + return new BandwidthLimitsMessage(); default: throw new I2CPMessageException("The type " + type + " is an unknown I2CP message"); } diff --git a/router/java/src/net/i2p/router/client/ClientMessageEventListener.java b/router/java/src/net/i2p/router/client/ClientMessageEventListener.java index 0dcc818709..cf49ebcd48 100644 --- a/router/java/src/net/i2p/router/client/ClientMessageEventListener.java +++ b/router/java/src/net/i2p/router/client/ClientMessageEventListener.java @@ -11,10 +11,12 @@ package net.i2p.router.client; import java.util.Properties; import net.i2p.data.Payload; +import net.i2p.data.i2cp.BandwidthLimitsMessage; import net.i2p.data.i2cp.CreateLeaseSetMessage; import net.i2p.data.i2cp.CreateSessionMessage; import net.i2p.data.i2cp.DestLookupMessage; import net.i2p.data.i2cp.DestroySessionMessage; +import net.i2p.data.i2cp.GetBandwidthLimitsMessage; import net.i2p.data.i2cp.GetDateMessage; import net.i2p.data.i2cp.I2CPMessage; import net.i2p.data.i2cp.I2CPMessageException; @@ -93,6 +95,9 @@ class ClientMessageEventListener implements I2CPMessageReader.I2CPMessageEventLi case ReconfigureSessionMessage.MESSAGE_TYPE: handleReconfigureSession(reader, (ReconfigureSessionMessage)message); break; + case GetBandwidthLimitsMessage.MESSAGE_TYPE: + handleGetBWLimits(reader, (GetBandwidthLimitsMessage)message); + break; default: if (_log.shouldLog(Log.ERROR)) _log.error("Unhandled I2CP type received: " + message.getType()); @@ -274,6 +279,24 @@ class ClientMessageEventListener implements I2CPMessageReader.I2CPMessageEventLi } } + /** + * Divide router limit by 2 for overhead. + * This could someday give a different answer to each client. + * But it's not enforced anywhere. + */ + private void handleGetBWLimits(I2CPMessageReader reader, GetBandwidthLimitsMessage message) { + if (_log.shouldLog(Log.INFO)) + _log.info("Got BW Limits request"); + int in = _context.bandwidthLimiter().getInboundKBytesPerSecond() / 2; + int out = _context.bandwidthLimiter().getOutboundKBytesPerSecond() / 2; + BandwidthLimitsMessage msg = new BandwidthLimitsMessage(in, out); + try { + _runner.doSend(msg); + } catch (I2CPMessageException ime) { + _log.error("Error writing out the session status message", ime); + } + } + // this *should* be mod 65536, but UnsignedInteger is still b0rked. FIXME private final static int MAX_SESSION_ID = 32767; -- GitLab