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