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 {
socket.close();
} finally {
connectionManager.connectionClosed();
connectionManager.connectionClosed(this);
}
}
}

View File

@@ -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.
* <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();
}

View File

@@ -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<DuplexTransportConnection> 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");
}
}
}

View File

@@ -219,25 +219,12 @@ abstract class BluetoothPlugin<SS> 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<SS> 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<SS> 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<SS> 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<SS> 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);
};
}

View File

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