From c00bf2b2cd4b2f0f7eb441937949dc23b5586449 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Fri, 23 Feb 2018 12:06:54 +0000 Subject: [PATCH] Close old connections to stay within limit. --- .../AndroidBluetoothTransportConnection.java | 2 +- .../bluetooth/BluetoothConnectionManager.java | 24 ++++-- .../BluetoothConnectionManagerImpl.java | 80 ++++++++++++++----- .../plugin/bluetooth/BluetoothPlugin.java | 33 ++------ .../JavaBluetoothTransportConnection.java | 2 +- 5 files changed, 86 insertions(+), 55 deletions(-) diff --git a/bramble-android/src/main/java/org/briarproject/bramble/plugin/bluetooth/AndroidBluetoothTransportConnection.java b/bramble-android/src/main/java/org/briarproject/bramble/plugin/bluetooth/AndroidBluetoothTransportConnection.java index 15373f213..a9689b5a8 100644 --- a/bramble-android/src/main/java/org/briarproject/bramble/plugin/bluetooth/AndroidBluetoothTransportConnection.java +++ b/bramble-android/src/main/java/org/briarproject/bramble/plugin/bluetooth/AndroidBluetoothTransportConnection.java @@ -40,7 +40,7 @@ class AndroidBluetoothTransportConnection try { socket.close(); } finally { - connectionManager.connectionClosed(); + connectionManager.connectionClosed(this); } } } diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothConnectionManager.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothConnectionManager.java index c6e722396..7c8726230 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothConnectionManager.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothConnectionManager.java @@ -1,26 +1,36 @@ package org.briarproject.bramble.plugin.bluetooth; +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; +import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection; + +@NotNullByDefault interface BluetoothConnectionManager { /** * Returns true if a contact connection can be opened without exceeding - * the connection limit. + * the connection limit. This method does not need to be called for key + * exchange connections. */ boolean canOpenConnection(); /** - * Increments the number of open connections and returns true if the new - * connection can be kept open without exceeding the connection limit. + * Passes a newly opened connection to the manager. The manager may close + * the new connection or another connection to stay within the connection + * limit. + *

+ * Returns false if the manager has closed the new connection (this will + * never be the case for key exchange connections). */ - boolean connectionOpened(); + boolean connectionOpened(DuplexTransportConnection conn, + boolean isForKeyExchange); /** - * Decrements the number of open connections. + * Informs the manager that the given connection has been closed. */ - void connectionClosed(); + void connectionClosed(DuplexTransportConnection conn); /** - * Resets the number of open connections. + * Informs the manager that all connections have been closed. */ void allConnectionsClosed(); } diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothConnectionManagerImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothConnectionManagerImpl.java index 7d40e7831..e1dd7d675 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothConnectionManagerImpl.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothConnectionManagerImpl.java @@ -1,10 +1,19 @@ package org.briarproject.bramble.plugin.bluetooth; -import java.util.concurrent.atomic.AtomicInteger; +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; +import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection; + +import java.io.IOException; +import java.util.LinkedList; import java.util.logging.Logger; -import static java.util.logging.Level.INFO; +import javax.annotation.concurrent.ThreadSafe; +import static java.util.logging.Level.INFO; +import static java.util.logging.Level.WARNING; + +@NotNullByDefault +@ThreadSafe class BluetoothConnectionManagerImpl implements BluetoothConnectionManager { private static final int MAX_OPEN_CONNECTIONS = 5; @@ -12,34 +21,69 @@ class BluetoothConnectionManagerImpl implements BluetoothConnectionManager { private static final Logger LOG = Logger.getLogger(BluetoothConnectionManagerImpl.class.getName()); - private final AtomicInteger openConnections = new AtomicInteger(0); + private final Object lock = new Object(); + private final LinkedList connections = + new LinkedList<>(); // Locking: lock @Override public boolean canOpenConnection() { - int open = openConnections.get(); - if (LOG.isLoggable(INFO)) - LOG.info(open + " open connections"); - return open < MAX_OPEN_CONNECTIONS; + synchronized (lock) { + int open = connections.size(); + if (LOG.isLoggable(INFO)) LOG.info(open + " open connections"); + return open < MAX_OPEN_CONNECTIONS; + } } @Override - public boolean connectionOpened() { - int open = openConnections.incrementAndGet(); - if (LOG.isLoggable(INFO)) - LOG.info("Connection opened, " + open + " open"); - return open <= MAX_OPEN_CONNECTIONS; + public boolean connectionOpened(DuplexTransportConnection conn, + boolean isForKeyExchange) { + DuplexTransportConnection close = null; + synchronized (lock) { + int open = connections.size(); + boolean accept = isForKeyExchange || open < MAX_OPEN_CONNECTIONS; + if (accept) { + if (LOG.isLoggable(INFO)) + LOG.info("Accepting connection, " + (open + 1) + " open"); + connections.add(conn); + if (open == MAX_OPEN_CONNECTIONS) { + LOG.info("Closing old connection to stay within limit"); + close = connections.poll(); + } + } else { + if (LOG.isLoggable(INFO)) + LOG.info("Refusing connection, " + open + " open"); + close = conn; + } + } + if (close != null) tryToClose(close); + return close != conn; + } + + private void tryToClose(DuplexTransportConnection conn) { + try { + conn.getWriter().dispose(false); + conn.getReader().dispose(false, false); + } catch (IOException e) { + if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); + } } @Override - public void connectionClosed() { - int open = openConnections.decrementAndGet(); - if (LOG.isLoggable(INFO)) - LOG.info("Connection closed, " + open + " open"); + public void connectionClosed(DuplexTransportConnection conn) { + synchronized (lock) { + connections.remove(conn); + if (LOG.isLoggable(INFO)) { + int open = connections.size(); + LOG.info("Connection closed, " + open + " open"); + } + } } @Override public void allConnectionsClosed() { - LOG.info("All connections closed"); - openConnections.set(0); + synchronized (lock) { + connections.clear(); + LOG.info("All connections closed"); + } } } diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java index 68c64efc3..594bcff94 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java @@ -219,25 +219,12 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener { return; } backoff.reset(); - if (connectionManager.connectionOpened()) { + if (connectionManager.connectionOpened(conn, false)) callback.incomingConnectionCreated(conn); - } else { - LOG.info("Closing incoming connection"); - tryToCloseUnusedConnection(conn); - } if (!running) return; } } - private void tryToCloseUnusedConnection(DuplexTransportConnection conn) { - try { - conn.getWriter().dispose(false); - conn.getReader().dispose(false, false); - } catch (IOException e) { - if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); - } - } - @Override public void stop() { running = false; @@ -284,12 +271,8 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener { DuplexTransportConnection conn = connect(address, uuid); if (conn != null) { backoff.reset(); - if (connectionManager.connectionOpened()) { + if (connectionManager.connectionOpened(conn, false)) callback.outgoingConnectionCreated(c, conn); - } else { - LOG.info("Closing outgoing connection"); - tryToCloseUnusedConnection(conn); - } } }); } @@ -341,13 +324,7 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener { DuplexTransportConnection conn = connect(address, uuid); if (conn == null) return null; // TODO: Why don't we reset the backoff here? - if (connectionManager.connectionOpened()) { - return conn; - } else { - LOG.info("Closing outgoing connection"); - tryToCloseUnusedConnection(conn); - return null; - } + return connectionManager.connectionOpened(conn, false) ? conn : null; } @Override @@ -399,7 +376,7 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener { LOG.info("Connecting to key agreement UUID " + uuid); DuplexTransportConnection conn = connect(address, uuid); // The connection limit doesn't apply to key agreement - if (conn != null) connectionManager.connectionOpened(); + if (conn != null) connectionManager.connectionOpened(conn, true); return conn; } @@ -456,7 +433,7 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener { if (LOG.isLoggable(INFO)) LOG.info(ID.getString() + ": Incoming connection"); // The connection limit doesn't apply to key agreement - connectionManager.connectionOpened(); + connectionManager.connectionOpened(conn, true); return new KeyAgreementConnection(conn, ID); }; } diff --git a/bramble-j2se/src/main/java/org/briarproject/bramble/plugin/bluetooth/JavaBluetoothTransportConnection.java b/bramble-j2se/src/main/java/org/briarproject/bramble/plugin/bluetooth/JavaBluetoothTransportConnection.java index 06b894f12..c94d7524a 100644 --- a/bramble-j2se/src/main/java/org/briarproject/bramble/plugin/bluetooth/JavaBluetoothTransportConnection.java +++ b/bramble-j2se/src/main/java/org/briarproject/bramble/plugin/bluetooth/JavaBluetoothTransportConnection.java @@ -40,7 +40,7 @@ class JavaBluetoothTransportConnection try { stream.close(); } finally { - connectionManager.connectionClosed(); + connectionManager.connectionClosed(this); } } }