Close old connections to stay within limit.

This commit is contained in:
akwizgran
2018-02-23 12:06:54 +00:00
parent 55150fe02a
commit c00bf2b2cd
5 changed files with 86 additions and 55 deletions

View File

@@ -40,7 +40,7 @@ class AndroidBluetoothTransportConnection
try { try {
socket.close(); socket.close();
} finally { } finally {
connectionManager.connectionClosed(); connectionManager.connectionClosed(this);
} }
} }
} }

View File

@@ -1,26 +1,36 @@
package org.briarproject.bramble.plugin.bluetooth; package org.briarproject.bramble.plugin.bluetooth;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
@NotNullByDefault
interface BluetoothConnectionManager { interface BluetoothConnectionManager {
/** /**
* Returns true if a contact connection can be opened without exceeding * 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(); boolean canOpenConnection();
/** /**
* Increments the number of open connections and returns true if the new * Passes a newly opened connection to the manager. The manager may close
* connection can be kept open without exceeding the connection limit. * the new connection or another connection to stay within the connection
* limit.
* <p/>
* 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(); void allConnectionsClosed();
} }

View File

@@ -1,10 +1,19 @@
package org.briarproject.bramble.plugin.bluetooth; 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 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 { class BluetoothConnectionManagerImpl implements BluetoothConnectionManager {
private static final int MAX_OPEN_CONNECTIONS = 5; private static final int MAX_OPEN_CONNECTIONS = 5;
@@ -12,34 +21,69 @@ class BluetoothConnectionManagerImpl implements BluetoothConnectionManager {
private static final Logger LOG = private static final Logger LOG =
Logger.getLogger(BluetoothConnectionManagerImpl.class.getName()); Logger.getLogger(BluetoothConnectionManagerImpl.class.getName());
private final AtomicInteger openConnections = new AtomicInteger(0); private final Object lock = new Object();
private final LinkedList<DuplexTransportConnection> connections =
new LinkedList<>(); // Locking: lock
@Override @Override
public boolean canOpenConnection() { public boolean canOpenConnection() {
int open = openConnections.get(); synchronized (lock) {
if (LOG.isLoggable(INFO)) int open = connections.size();
LOG.info(open + " open connections"); if (LOG.isLoggable(INFO)) LOG.info(open + " open connections");
return open < MAX_OPEN_CONNECTIONS; return open < MAX_OPEN_CONNECTIONS;
}
} }
@Override @Override
public boolean connectionOpened() { public boolean connectionOpened(DuplexTransportConnection conn,
int open = openConnections.incrementAndGet(); boolean isForKeyExchange) {
if (LOG.isLoggable(INFO)) DuplexTransportConnection close = null;
LOG.info("Connection opened, " + open + " open"); synchronized (lock) {
return open <= MAX_OPEN_CONNECTIONS; 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 @Override
public void connectionClosed() { public void connectionClosed(DuplexTransportConnection conn) {
int open = openConnections.decrementAndGet(); synchronized (lock) {
if (LOG.isLoggable(INFO)) connections.remove(conn);
LOG.info("Connection closed, " + open + " open"); if (LOG.isLoggable(INFO)) {
int open = connections.size();
LOG.info("Connection closed, " + open + " open");
}
}
} }
@Override @Override
public void allConnectionsClosed() { public void allConnectionsClosed() {
LOG.info("All connections closed"); synchronized (lock) {
openConnections.set(0); connections.clear();
LOG.info("All connections closed");
}
} }
} }

View File

@@ -219,25 +219,12 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
return; return;
} }
backoff.reset(); backoff.reset();
if (connectionManager.connectionOpened()) { if (connectionManager.connectionOpened(conn, false))
callback.incomingConnectionCreated(conn); callback.incomingConnectionCreated(conn);
} else {
LOG.info("Closing incoming connection");
tryToCloseUnusedConnection(conn);
}
if (!running) return; 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 @Override
public void stop() { public void stop() {
running = false; running = false;
@@ -284,12 +271,8 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
DuplexTransportConnection conn = connect(address, uuid); DuplexTransportConnection conn = connect(address, uuid);
if (conn != null) { if (conn != null) {
backoff.reset(); backoff.reset();
if (connectionManager.connectionOpened()) { if (connectionManager.connectionOpened(conn, false))
callback.outgoingConnectionCreated(c, conn); callback.outgoingConnectionCreated(c, conn);
} else {
LOG.info("Closing outgoing connection");
tryToCloseUnusedConnection(conn);
}
} }
}); });
} }
@@ -341,13 +324,7 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
DuplexTransportConnection conn = connect(address, uuid); DuplexTransportConnection conn = connect(address, uuid);
if (conn == null) return null; if (conn == null) return null;
// TODO: Why don't we reset the backoff here? // TODO: Why don't we reset the backoff here?
if (connectionManager.connectionOpened()) { return connectionManager.connectionOpened(conn, false) ? conn : null;
return conn;
} else {
LOG.info("Closing outgoing connection");
tryToCloseUnusedConnection(conn);
return null;
}
} }
@Override @Override
@@ -399,7 +376,7 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
LOG.info("Connecting to key agreement UUID " + uuid); LOG.info("Connecting to key agreement UUID " + uuid);
DuplexTransportConnection conn = connect(address, uuid); DuplexTransportConnection conn = connect(address, uuid);
// The connection limit doesn't apply to key agreement // The connection limit doesn't apply to key agreement
if (conn != null) connectionManager.connectionOpened(); if (conn != null) connectionManager.connectionOpened(conn, true);
return conn; return conn;
} }
@@ -456,7 +433,7 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO))
LOG.info(ID.getString() + ": Incoming connection"); LOG.info(ID.getString() + ": Incoming connection");
// The connection limit doesn't apply to key agreement // The connection limit doesn't apply to key agreement
connectionManager.connectionOpened(); connectionManager.connectionOpened(conn, true);
return new KeyAgreementConnection(conn, ID); return new KeyAgreementConnection(conn, ID);
}; };
} }

View File

@@ -40,7 +40,7 @@ class JavaBluetoothTransportConnection
try { try {
stream.close(); stream.close();
} finally { } finally {
connectionManager.connectionClosed(); connectionManager.connectionClosed(this);
} }
} }
} }