Compare commits

..

13 Commits

Author SHA1 Message Date
akwizgran
173b6006c4 Don't treat an incoming connection as an attempt to raise the limit. 2020-05-11 17:15:25 +01:00
akwizgran
99edb893f7 Check for stability whenever connections are closed. 2020-05-11 17:15:25 +01:00
akwizgran
f063feedd4 Simplify backoff. 2020-05-11 17:15:25 +01:00
akwizgran
126f515760 Move responsibility for closing connections from limiter to plugin. 2020-05-11 17:15:25 +01:00
akwizgran
e2b61483d6 Always accept incoming connections. 2020-05-11 17:15:25 +01:00
akwizgran
9771825c45 Back off between attempts to raise connection limit. 2020-05-11 17:15:24 +01:00
akwizgran
e376744487 Update constructor args. 2020-05-11 17:15:24 +01:00
akwizgran
13cca9ca61 Occasionally try to raise the limit by allowing an extra connection. 2020-05-11 17:15:24 +01:00
akwizgran
e464f9e7bd Close connections cleanly when starting key agreement. 2020-05-11 17:15:24 +01:00
akwizgran
bd86ff2d5f Let the limiter know whether connections closed cleanly. 2020-05-11 17:15:24 +01:00
akwizgran
bda3b2100a Raise the connection limit if connections are stable. 2020-05-11 17:15:24 +01:00
akwizgran
104a82aea9 Add unit test for connection limiter. 2020-05-11 17:15:24 +01:00
akwizgran
d905451f48 Impose a fixed limit on the number of Bluetooth connections. 2020-05-11 17:15:24 +01:00
68 changed files with 1490 additions and 2501 deletions

View File

@@ -50,7 +50,7 @@ class AndroidBluetoothTransportConnection
try {
socket.close();
} finally {
connectionLimiter.connectionClosed(this);
connectionLimiter.connectionClosed(this, exception);
}
}
}

View File

@@ -1,130 +0,0 @@
package org.briarproject.bramble.api.connection;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.PluginConfig;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.event.ConnectionClosedEvent;
import org.briarproject.bramble.api.plugin.event.ConnectionOpenedEvent;
import org.briarproject.bramble.api.plugin.event.ContactConnectedEvent;
import org.briarproject.bramble.api.plugin.event.ContactDisconnectedEvent;
import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionClosedEvent;
import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionOpenedEvent;
import org.briarproject.bramble.api.sync.Priority;
import java.util.Collection;
/**
* Keeps track of which contacts are currently connected by which transports.
*/
@NotNullByDefault
public interface ConnectionRegistry {
/**
* Registers an incoming connection from the given contact over the given
* transport. The connection's {@link Priority priority} can be set later
* via {@link #setPriority(ContactId, TransportId, InterruptibleConnection,
* Priority)} if a priority record is received from the contact.
* <p>
* Broadcasts {@link ConnectionOpenedEvent}. Also broadcasts
* {@link ContactConnectedEvent} if this is the only connection with the
* contact.
*/
void registerIncomingConnection(ContactId c, TransportId t,
InterruptibleConnection conn);
/**
* Registers an outgoing connection to the given contact over the given
* transport.
* <p>
* Broadcasts {@link ConnectionOpenedEvent}. Also broadcasts
* {@link ContactConnectedEvent} if this is the only connection with the
* contact.
* <p>
* If the registry has any "better" connections with the given contact, the
* given connection will be interrupted. If the registry has any "worse"
* connections with the given contact, those connections will be
* interrupted.
* <p>
* Connection A is considered "better" than connection B if both
* connections have had their priorities set, and either A's transport is
* {@link PluginConfig#getTransportPreferences() preferred} to B's, or
* they use the same transport and A has higher {@link Priority priority}
* than B.
* <p>
* For backward compatibility, connections without priorities are not
* considered better or worse than other connections.
*/
void registerOutgoingConnection(ContactId c, TransportId t,
InterruptibleConnection conn, Priority priority);
/**
* Unregisters a connection with the given contact over the given transport.
* <p>
* Broadcasts {@link ConnectionClosedEvent}. Also broadcasts
* {@link ContactDisconnectedEvent} if this is the only connection with
* the contact.
*/
void unregisterConnection(ContactId c, TransportId t,
InterruptibleConnection conn, boolean incoming, boolean exception);
/**
* Sets the {@link Priority priority} of a connection that was previously
* registered via {@link #registerIncomingConnection(ContactId, TransportId,
* InterruptibleConnection)}.
* <p>
* If the registry has any "better" connections with the given contact, the
* given connection will be interrupted. If the registry has any "worse"
* connections with the given contact, those connections will be
* interrupted.
* <p>
* Connection A is considered "better" than connection B if both
* connections have had their priorities set, and either A's transport is
* {@link PluginConfig#getTransportPreferences() preferred} to B's, or
* they use the same transport and A has higher {@link Priority priority}
* than B.
* <p>
* For backward compatibility, connections without priorities are not
* considered better or worse than other connections.
*/
void setPriority(ContactId c, TransportId t, InterruptibleConnection conn,
Priority priority);
/**
* Returns any contacts that are connected via the given transport.
*/
Collection<ContactId> getConnectedContacts(TransportId t);
/**
* Returns any contacts that are connected via the given transport or any
* {@link PluginConfig#getTransportPreferences() better} transport.
*/
Collection<ContactId> getConnectedOrBetterContacts(TransportId t);
/**
* Returns true if the given contact is connected via the given transport.
*/
boolean isConnected(ContactId c, TransportId t);
/**
* Returns true if the given contact is connected via any transport.
*/
boolean isConnected(ContactId c);
/**
* Registers a connection with the given pending contact. Broadcasts
* {@link RendezvousConnectionOpenedEvent} if this is the only connection
* with the pending contact.
*
* @return True if this is the only connection with the pending contact,
* false if it is redundant and should be closed
*/
boolean registerConnection(PendingContactId p);
/**
* Unregisters a connection with the given pending contact. Broadcasts
* {@link RendezvousConnectionClosedEvent}.
*/
void unregisterConnection(PendingContactId p, boolean success);
}

View File

@@ -1,19 +0,0 @@
package org.briarproject.bramble.api.connection;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
/**
* A duplex sync connection that can be closed by interrupting its outgoing
* sync session.
*/
@NotNullByDefault
public interface InterruptibleConnection {
/**
* Interrupts the connection's outgoing sync session. If the underlying
* transport connection is alive and the remote peer is cooperative, this
* should result in both sync sessions ending and the connection being
* cleanly closed.
*/
void interruptOutgoingSession();
}

View File

@@ -1,11 +1,8 @@
package org.briarproject.bramble.api.connection;
package org.briarproject.bramble.api.plugin;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportConnectionReader;
import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
@NotNullByDefault

View File

@@ -0,0 +1,67 @@
package org.briarproject.bramble.api.plugin;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.event.ConnectionClosedEvent;
import org.briarproject.bramble.api.plugin.event.ConnectionOpenedEvent;
import org.briarproject.bramble.api.plugin.event.ContactConnectedEvent;
import org.briarproject.bramble.api.plugin.event.ContactDisconnectedEvent;
import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionClosedEvent;
import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionOpenedEvent;
import java.util.Collection;
/**
* Keeps track of which contacts are currently connected by which transports.
*/
@NotNullByDefault
public interface ConnectionRegistry {
/**
* Registers a connection with the given contact over the given transport.
* Broadcasts {@link ConnectionOpenedEvent}. Also broadcasts
* {@link ContactConnectedEvent} if this is the only connection with the
* contact.
*/
void registerConnection(ContactId c, TransportId t, boolean incoming);
/**
* Unregisters a connection with the given contact over the given transport.
* Broadcasts {@link ConnectionClosedEvent}. Also broadcasts
* {@link ContactDisconnectedEvent} if this is the only connection with
* the contact.
*/
void unregisterConnection(ContactId c, TransportId t, boolean incoming);
/**
* Returns any contacts that are connected via the given transport.
*/
Collection<ContactId> getConnectedContacts(TransportId t);
/**
* Returns true if the given contact is connected via the given transport.
*/
boolean isConnected(ContactId c, TransportId t);
/**
* Returns true if the given contact is connected via any transport.
*/
boolean isConnected(ContactId c);
/**
* Registers a connection with the given pending contact. Broadcasts
* {@link RendezvousConnectionOpenedEvent} if this is the only connection
* with the pending contact.
*
* @return True if this is the only connection with the pending contact,
* false if it is redundant and should be closed
*/
boolean registerConnection(PendingContactId p);
/**
* Unregisters a connection with the given pending contact. Broadcasts
* {@link RendezvousConnectionClosedEvent}.
*/
void unregisterConnection(PendingContactId p, boolean success);
}

View File

@@ -5,8 +5,6 @@ import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory;
import java.util.Collection;
import java.util.List;
import java.util.Map;
@NotNullByDefault
public interface PluginConfig {
@@ -16,11 +14,4 @@ public interface PluginConfig {
Collection<SimplexPluginFactory> getSimplexFactories();
boolean shouldPoll();
/**
* Returns a map representing transport preferences. For each entry in the
* map, connections via the transports identified by the value are
* preferred to connections via the transport identified by the key.
*/
Map<TransportId, List<TransportId>> getTransportPreferences();
}

View File

@@ -13,14 +13,13 @@ public class ConnectionClosedEvent extends Event {
private final ContactId contactId;
private final TransportId transportId;
private final boolean incoming, exception;
private final boolean incoming;
public ConnectionClosedEvent(ContactId contactId, TransportId transportId,
boolean incoming, boolean exception) {
boolean incoming) {
this.contactId = contactId;
this.transportId = transportId;
this.incoming = incoming;
this.exception = exception;
}
public ContactId getContactId() {
@@ -34,8 +33,4 @@ public class ConnectionClosedEvent extends Event {
public boolean isIncoming() {
return incoming;
}
public boolean isException() {
return exception;
}
}

View File

@@ -1,23 +0,0 @@
package org.briarproject.bramble.api.sync;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
/**
* A record containing a nonce for choosing between redundant sessions.
*/
@Immutable
@NotNullByDefault
public class Priority {
private final byte[] nonce;
public Priority(byte[] nonce) {
this.nonce = nonce;
}
public byte[] getNonce() {
return nonce;
}
}

View File

@@ -1,13 +0,0 @@
package org.briarproject.bramble.api.sync;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
/**
* An interface for handling a {@link Priority} record received by an
* incoming {@link SyncSession}.
*/
@NotNullByDefault
public interface PriorityHandler {
void handle(Priority p);
}

View File

@@ -10,5 +10,4 @@ public interface RecordTypes {
byte OFFER = 2;
byte REQUEST = 3;
byte VERSIONS = 4;
byte PRIORITY = 5;
}

View File

@@ -49,10 +49,4 @@ public interface SyncConstants {
* simultaneously.
*/
int MAX_SUPPORTED_VERSIONS = 10;
/**
* The length of the priority nonce used for choosing between redundant
* connections.
*/
int PRIORITY_NONCE_BYTES = 16;
}

View File

@@ -28,8 +28,4 @@ public interface SyncRecordReader {
boolean hasVersions() throws IOException;
Versions readVersions() throws IOException;
boolean hasPriority() throws IOException;
Priority readPriority() throws IOException;
}

View File

@@ -17,7 +17,5 @@ public interface SyncRecordWriter {
void writeVersions(Versions v) throws IOException;
void writePriority(Priority p) throws IOException;
void flush() throws IOException;
}

View File

@@ -7,18 +7,14 @@ import org.briarproject.bramble.api.transport.StreamWriter;
import java.io.InputStream;
import javax.annotation.Nullable;
@NotNullByDefault
public interface SyncSessionFactory {
SyncSession createIncomingSession(ContactId c, InputStream in,
PriorityHandler handler);
SyncSession createIncomingSession(ContactId c, InputStream in);
SyncSession createSimplexOutgoingSession(ContactId c, TransportId t,
int maxLatency, StreamWriter streamWriter);
SyncSession createDuplexOutgoingSession(ContactId c, TransportId t,
int maxLatency, int maxIdleTime, StreamWriter streamWriter,
@Nullable Priority priority);
int maxLatency, int maxIdleTime, StreamWriter streamWriter);
}

View File

@@ -1,7 +1,6 @@
package org.briarproject.bramble;
import org.briarproject.bramble.client.ClientModule;
import org.briarproject.bramble.connection.ConnectionModule;
import org.briarproject.bramble.contact.ContactModule;
import org.briarproject.bramble.crypto.CryptoExecutorModule;
import org.briarproject.bramble.crypto.CryptoModule;
@@ -29,7 +28,6 @@ import dagger.Module;
@Module(includes = {
ClientModule.class,
ConnectionModule.class,
ContactModule.class,
CryptoModule.class,
CryptoExecutorModule.class,

View File

@@ -1,79 +0,0 @@
package org.briarproject.bramble.connection;
import org.briarproject.bramble.api.connection.ConnectionRegistry;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportConnectionReader;
import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.transport.KeyManager;
import org.briarproject.bramble.api.transport.StreamContext;
import org.briarproject.bramble.api.transport.StreamReaderFactory;
import org.briarproject.bramble.api.transport.StreamWriterFactory;
import java.io.IOException;
import java.io.InputStream;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
import static org.briarproject.bramble.util.IoUtils.read;
import static org.briarproject.bramble.util.LogUtils.logException;
@NotNullByDefault
abstract class Connection {
protected static final Logger LOG = getLogger(Connection.class.getName());
final KeyManager keyManager;
final ConnectionRegistry connectionRegistry;
final StreamReaderFactory streamReaderFactory;
final StreamWriterFactory streamWriterFactory;
Connection(KeyManager keyManager, ConnectionRegistry connectionRegistry,
StreamReaderFactory streamReaderFactory,
StreamWriterFactory streamWriterFactory) {
this.keyManager = keyManager;
this.connectionRegistry = connectionRegistry;
this.streamReaderFactory = streamReaderFactory;
this.streamWriterFactory = streamWriterFactory;
}
@Nullable
StreamContext recogniseTag(TransportConnectionReader reader,
TransportId transportId) {
StreamContext ctx;
try {
byte[] tag = readTag(reader.getInputStream());
return keyManager.getStreamContext(transportId, tag);
} catch (IOException | DbException e) {
logException(LOG, WARNING, e);
return null;
}
}
private byte[] readTag(InputStream in) throws IOException {
byte[] tag = new byte[TAG_LENGTH];
read(in, tag);
return tag;
}
void disposeOnError(TransportConnectionReader reader, boolean recognised) {
try {
reader.dispose(true, recognised);
} catch (IOException e) {
logException(LOG, WARNING, e);
}
}
void disposeOnError(TransportConnectionWriter writer) {
try {
writer.dispose(true);
} catch (IOException e) {
logException(LOG, WARNING, e);
}
}
}

View File

@@ -1,114 +0,0 @@
package org.briarproject.bramble.connection;
import org.briarproject.bramble.api.connection.ConnectionManager;
import org.briarproject.bramble.api.connection.ConnectionRegistry;
import org.briarproject.bramble.api.contact.ContactExchangeManager;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.HandshakeManager;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportConnectionReader;
import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import org.briarproject.bramble.api.properties.TransportPropertyManager;
import org.briarproject.bramble.api.sync.SyncSessionFactory;
import org.briarproject.bramble.api.transport.KeyManager;
import org.briarproject.bramble.api.transport.StreamReaderFactory;
import org.briarproject.bramble.api.transport.StreamWriterFactory;
import java.security.SecureRandom;
import java.util.concurrent.Executor;
import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
@Immutable
@NotNullByDefault
class ConnectionManagerImpl implements ConnectionManager {
private final Executor ioExecutor;
private final KeyManager keyManager;
private final StreamReaderFactory streamReaderFactory;
private final StreamWriterFactory streamWriterFactory;
private final SyncSessionFactory syncSessionFactory;
private final HandshakeManager handshakeManager;
private final ContactExchangeManager contactExchangeManager;
private final ConnectionRegistry connectionRegistry;
private final TransportPropertyManager transportPropertyManager;
private final SecureRandom secureRandom;
@Inject
ConnectionManagerImpl(@IoExecutor Executor ioExecutor,
KeyManager keyManager, StreamReaderFactory streamReaderFactory,
StreamWriterFactory streamWriterFactory,
SyncSessionFactory syncSessionFactory,
HandshakeManager handshakeManager,
ContactExchangeManager contactExchangeManager,
ConnectionRegistry connectionRegistry,
TransportPropertyManager transportPropertyManager,
SecureRandom secureRandom) {
this.ioExecutor = ioExecutor;
this.keyManager = keyManager;
this.streamReaderFactory = streamReaderFactory;
this.streamWriterFactory = streamWriterFactory;
this.syncSessionFactory = syncSessionFactory;
this.handshakeManager = handshakeManager;
this.contactExchangeManager = contactExchangeManager;
this.connectionRegistry = connectionRegistry;
this.transportPropertyManager = transportPropertyManager;
this.secureRandom = secureRandom;
}
@Override
public void manageIncomingConnection(TransportId t,
TransportConnectionReader r) {
ioExecutor.execute(new IncomingSimplexSyncConnection(keyManager,
connectionRegistry, streamReaderFactory, streamWriterFactory,
syncSessionFactory, transportPropertyManager, t, r));
}
@Override
public void manageIncomingConnection(TransportId t,
DuplexTransportConnection d) {
ioExecutor.execute(new IncomingDuplexSyncConnection(keyManager,
connectionRegistry, streamReaderFactory, streamWriterFactory,
syncSessionFactory, transportPropertyManager, ioExecutor,
t, d));
}
@Override
public void manageIncomingConnection(PendingContactId p, TransportId t,
DuplexTransportConnection d) {
ioExecutor.execute(new IncomingHandshakeConnection(keyManager,
connectionRegistry, streamReaderFactory, streamWriterFactory,
handshakeManager, contactExchangeManager, this, p, t, d));
}
@Override
public void manageOutgoingConnection(ContactId c, TransportId t,
TransportConnectionWriter w) {
ioExecutor.execute(new OutgoingSimplexSyncConnection(keyManager,
connectionRegistry, streamReaderFactory, streamWriterFactory,
syncSessionFactory, transportPropertyManager, c, t, w));
}
@Override
public void manageOutgoingConnection(ContactId c, TransportId t,
DuplexTransportConnection d) {
ioExecutor.execute(new OutgoingDuplexSyncConnection(keyManager,
connectionRegistry, streamReaderFactory, streamWriterFactory,
syncSessionFactory, transportPropertyManager, ioExecutor,
secureRandom, c, t, d));
}
@Override
public void manageOutgoingConnection(PendingContactId p, TransportId t,
DuplexTransportConnection d) {
ioExecutor.execute(new OutgoingHandshakeConnection(keyManager,
connectionRegistry, streamReaderFactory, streamWriterFactory,
handshakeManager, contactExchangeManager, this, p, t, d));
}
}

View File

@@ -1,26 +0,0 @@
package org.briarproject.bramble.connection;
import org.briarproject.bramble.api.connection.ConnectionManager;
import org.briarproject.bramble.api.connection.ConnectionRegistry;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
@Module
public class ConnectionModule {
@Provides
ConnectionManager provideConnectionManager(
ConnectionManagerImpl connectionManager) {
return connectionManager;
}
@Provides
@Singleton
ConnectionRegistry provideConnectionRegistry(
ConnectionRegistryImpl connectionRegistry) {
return connectionRegistry;
}
}

View File

@@ -1,289 +0,0 @@
package org.briarproject.bramble.connection;
import org.briarproject.bramble.api.Bytes;
import org.briarproject.bramble.api.connection.ConnectionRegistry;
import org.briarproject.bramble.api.connection.InterruptibleConnection;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.PluginConfig;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.event.ConnectionClosedEvent;
import org.briarproject.bramble.api.plugin.event.ConnectionOpenedEvent;
import org.briarproject.bramble.api.plugin.event.ContactConnectedEvent;
import org.briarproject.bramble.api.plugin.event.ContactDisconnectedEvent;
import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionClosedEvent;
import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionOpenedEvent;
import org.briarproject.bramble.api.sync.Priority;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.annotation.concurrent.GuardedBy;
import javax.annotation.concurrent.ThreadSafe;
import javax.inject.Inject;
import static java.util.Collections.emptyList;
import static java.util.logging.Level.INFO;
import static java.util.logging.Logger.getLogger;
@ThreadSafe
@NotNullByDefault
class ConnectionRegistryImpl implements ConnectionRegistry {
private static final Logger LOG =
getLogger(ConnectionRegistryImpl.class.getName());
private final EventBus eventBus;
private final Map<TransportId, List<TransportId>> transportPrefs;
private final Object lock = new Object();
@GuardedBy("lock")
private final Map<ContactId, List<ConnectionRecord>> contactConnections;
@GuardedBy("lock")
private final Set<PendingContactId> connectedPendingContacts;
@Inject
ConnectionRegistryImpl(EventBus eventBus, PluginConfig pluginConfig) {
this.eventBus = eventBus;
transportPrefs = pluginConfig.getTransportPreferences();
contactConnections = new HashMap<>();
connectedPendingContacts = new HashSet<>();
}
@Override
public void registerIncomingConnection(ContactId c, TransportId t,
InterruptibleConnection conn) {
if (LOG.isLoggable(INFO)) {
LOG.info("Incoming connection registered: " + t);
}
registerConnection(c, t, conn, true);
}
@Override
public void registerOutgoingConnection(ContactId c, TransportId t,
InterruptibleConnection conn, Priority priority) {
if (LOG.isLoggable(INFO)) {
LOG.info("Outgoing connection registered: " + t);
}
registerConnection(c, t, conn, false);
setPriority(c, t, conn, priority);
}
private void registerConnection(ContactId c, TransportId t,
InterruptibleConnection conn, boolean incoming) {
if (LOG.isLoggable(INFO)) {
if (incoming) LOG.info("Incoming connection registered: " + t);
else LOG.info("Outgoing connection registered: " + t);
}
boolean firstConnection;
synchronized (lock) {
List<ConnectionRecord> recs = contactConnections.get(c);
if (recs == null) {
recs = new ArrayList<>();
contactConnections.put(c, recs);
}
firstConnection = recs.isEmpty();
recs.add(new ConnectionRecord(t, conn));
}
eventBus.broadcast(new ConnectionOpenedEvent(c, t, incoming));
if (firstConnection) {
LOG.info("Contact connected");
eventBus.broadcast(new ContactConnectedEvent(c));
}
}
@Override
public void setPriority(ContactId c, TransportId t,
InterruptibleConnection conn, Priority priority) {
if (LOG.isLoggable(INFO)) LOG.info("Setting connection priority: " + t);
List<InterruptibleConnection> toInterrupt;
boolean interruptNewConnection = false;
synchronized (lock) {
List<ConnectionRecord> recs = contactConnections.get(c);
if (recs == null) throw new IllegalArgumentException();
toInterrupt = new ArrayList<>(recs.size());
for (ConnectionRecord rec : recs) {
if (rec.conn == conn) {
// Store the priority of this connection
rec.priority = priority;
} else if (rec.priority != null) {
int compare = compareConnections(t, priority,
rec.transportId, rec.priority);
if (compare == -1) {
// The old connection is better than the new one
interruptNewConnection = true;
} else if (compare == 1 && !rec.interrupted) {
// The new connection is better than the old one
toInterrupt.add(rec.conn);
rec.interrupted = true;
}
}
}
}
if (interruptNewConnection) {
LOG.info("Interrupting new connection");
conn.interruptOutgoingSession();
}
for (InterruptibleConnection old : toInterrupt) {
LOG.info("Interrupting old connection");
old.interruptOutgoingSession();
}
}
private int compareConnections(TransportId tA, Priority pA, TransportId tB,
Priority pB) {
if (getBetterTransports(tA).contains(tB)) return -1;
if (getBetterTransports(tB).contains(tA)) return 1;
return tA.equals(tB) ? Bytes.compare(pA.getNonce(), pB.getNonce()) : 0;
}
private List<TransportId> getBetterTransports(TransportId t) {
List<TransportId> better = transportPrefs.get(t);
return better == null ? emptyList() : better;
}
@Override
public void unregisterConnection(ContactId c, TransportId t,
InterruptibleConnection conn, boolean incoming, boolean exception) {
if (LOG.isLoggable(INFO)) {
if (incoming) LOG.info("Incoming connection unregistered: " + t);
else LOG.info("Outgoing connection unregistered: " + t);
}
boolean lastConnection;
synchronized (lock) {
List<ConnectionRecord> recs = contactConnections.get(c);
if (recs == null || !recs.remove(new ConnectionRecord(t, conn)))
throw new IllegalArgumentException();
lastConnection = recs.isEmpty();
}
eventBus.broadcast(
new ConnectionClosedEvent(c, t, incoming, exception));
if (lastConnection) {
LOG.info("Contact disconnected");
eventBus.broadcast(new ContactDisconnectedEvent(c));
}
}
@Override
public Collection<ContactId> getConnectedContacts(TransportId t) {
synchronized (lock) {
List<ContactId> contactIds = new ArrayList<>();
for (Entry<ContactId, List<ConnectionRecord>> e :
contactConnections.entrySet()) {
for (ConnectionRecord rec : e.getValue()) {
if (rec.transportId.equals(t)) {
contactIds.add(e.getKey());
break;
}
}
}
if (LOG.isLoggable(INFO)) {
LOG.info(contactIds.size() + " contacts connected: " + t);
}
return contactIds;
}
}
@Override
public Collection<ContactId> getConnectedOrBetterContacts(TransportId t) {
synchronized (lock) {
List<TransportId> better = getBetterTransports(t);
List<ContactId> contactIds = new ArrayList<>();
for (Entry<ContactId, List<ConnectionRecord>> e :
contactConnections.entrySet()) {
for (ConnectionRecord rec : e.getValue()) {
if (rec.transportId.equals(t) ||
better.contains(rec.transportId)) {
contactIds.add(e.getKey());
break;
}
}
}
if (LOG.isLoggable(INFO)) {
LOG.info(contactIds.size()
+ " contacts connected or better: " + t);
}
return contactIds;
}
}
@Override
public boolean isConnected(ContactId c, TransportId t) {
synchronized (lock) {
List<ConnectionRecord> recs = contactConnections.get(c);
if (recs == null) return false;
for (ConnectionRecord rec : recs) {
if (rec.transportId.equals(t)) return true;
}
return false;
}
}
@Override
public boolean isConnected(ContactId c) {
synchronized (lock) {
List<ConnectionRecord> recs = contactConnections.get(c);
return recs != null && !recs.isEmpty();
}
}
@Override
public boolean registerConnection(PendingContactId p) {
boolean added;
synchronized (lock) {
added = connectedPendingContacts.add(p);
}
if (added) eventBus.broadcast(new RendezvousConnectionOpenedEvent(p));
return added;
}
@Override
public void unregisterConnection(PendingContactId p, boolean success) {
synchronized (lock) {
if (!connectedPendingContacts.remove(p))
throw new IllegalArgumentException();
}
eventBus.broadcast(new RendezvousConnectionClosedEvent(p, success));
}
private static class ConnectionRecord {
private final TransportId transportId;
private final InterruptibleConnection conn;
@GuardedBy("lock")
@Nullable
private Priority priority = null;
@GuardedBy("lock")
private boolean interrupted = false;
private ConnectionRecord(TransportId transportId,
InterruptibleConnection conn) {
this.transportId = transportId;
this.conn = conn;
}
@Override
public boolean equals(Object o) {
if (o instanceof ConnectionRecord) {
return conn == ((ConnectionRecord) o).conn;
} else {
return false;
}
}
@Override
public int hashCode() {
return conn.hashCode();
}
}
}

View File

@@ -1,109 +0,0 @@
package org.briarproject.bramble.connection;
import org.briarproject.bramble.api.connection.ConnectionRegistry;
import org.briarproject.bramble.api.connection.InterruptibleConnection;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportConnectionReader;
import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.properties.TransportPropertyManager;
import org.briarproject.bramble.api.sync.Priority;
import org.briarproject.bramble.api.sync.SyncSession;
import org.briarproject.bramble.api.sync.SyncSessionFactory;
import org.briarproject.bramble.api.transport.KeyManager;
import org.briarproject.bramble.api.transport.StreamContext;
import org.briarproject.bramble.api.transport.StreamReaderFactory;
import org.briarproject.bramble.api.transport.StreamWriter;
import org.briarproject.bramble.api.transport.StreamWriterFactory;
import java.io.IOException;
import java.util.concurrent.Executor;
import javax.annotation.Nullable;
import javax.annotation.concurrent.GuardedBy;
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
@NotNullByDefault
abstract class DuplexSyncConnection extends SyncConnection
implements InterruptibleConnection {
final Executor ioExecutor;
final TransportId transportId;
final TransportConnectionReader reader;
final TransportConnectionWriter writer;
final TransportProperties remote;
private final Object interruptLock = new Object();
@GuardedBy("interruptLock")
@Nullable
private SyncSession outgoingSession = null;
@GuardedBy("interruptLock")
private boolean interruptWaiting = false;
@Override
public void interruptOutgoingSession() {
SyncSession out = null;
synchronized (interruptLock) {
if (outgoingSession == null) interruptWaiting = true;
else out = outgoingSession;
}
if (out != null) out.interrupt();
}
void setOutgoingSession(SyncSession outgoingSession) {
boolean interruptWasWaiting = false;
synchronized (interruptLock) {
this.outgoingSession = outgoingSession;
if (interruptWaiting) {
interruptWasWaiting = true;
interruptWaiting = false;
}
}
if (interruptWasWaiting) outgoingSession.interrupt();
}
DuplexSyncConnection(KeyManager keyManager,
ConnectionRegistry connectionRegistry,
StreamReaderFactory streamReaderFactory,
StreamWriterFactory streamWriterFactory,
SyncSessionFactory syncSessionFactory,
TransportPropertyManager transportPropertyManager,
Executor ioExecutor, TransportId transportId,
DuplexTransportConnection connection) {
super(keyManager, connectionRegistry, streamReaderFactory,
streamWriterFactory, syncSessionFactory,
transportPropertyManager);
this.ioExecutor = ioExecutor;
this.transportId = transportId;
reader = connection.getReader();
writer = connection.getWriter();
remote = connection.getRemoteProperties();
}
void onReadError(boolean recognised) {
disposeOnError(reader, recognised);
disposeOnError(writer);
interruptOutgoingSession();
}
void onWriteError() {
disposeOnError(reader, true);
disposeOnError(writer);
}
SyncSession createDuplexOutgoingSession(StreamContext ctx,
TransportConnectionWriter w, @Nullable Priority priority)
throws IOException {
StreamWriter streamWriter = streamWriterFactory.createStreamWriter(
w.getOutputStream(), ctx);
ContactId c = requireNonNull(ctx.getContactId());
return syncSessionFactory.createDuplexOutgoingSession(c,
ctx.getTransportId(), w.getMaxLatency(), w.getMaxIdleTime(),
streamWriter, priority);
}
}

View File

@@ -1,72 +0,0 @@
package org.briarproject.bramble.connection;
import org.briarproject.bramble.api.connection.ConnectionManager;
import org.briarproject.bramble.api.connection.ConnectionRegistry;
import org.briarproject.bramble.api.contact.ContactExchangeManager;
import org.briarproject.bramble.api.contact.HandshakeManager;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportConnectionReader;
import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import org.briarproject.bramble.api.transport.KeyManager;
import org.briarproject.bramble.api.transport.StreamContext;
import org.briarproject.bramble.api.transport.StreamReaderFactory;
import org.briarproject.bramble.api.transport.StreamWriterFactory;
import javax.annotation.Nullable;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.util.LogUtils.logException;
@NotNullByDefault
abstract class HandshakeConnection extends Connection {
final HandshakeManager handshakeManager;
final ContactExchangeManager contactExchangeManager;
final ConnectionManager connectionManager;
final PendingContactId pendingContactId;
final TransportId transportId;
final DuplexTransportConnection connection;
final TransportConnectionReader reader;
final TransportConnectionWriter writer;
HandshakeConnection(KeyManager keyManager,
ConnectionRegistry connectionRegistry,
StreamReaderFactory streamReaderFactory,
StreamWriterFactory streamWriterFactory,
HandshakeManager handshakeManager,
ContactExchangeManager contactExchangeManager,
ConnectionManager connectionManager,
PendingContactId pendingContactId,
TransportId transportId, DuplexTransportConnection connection) {
super(keyManager, connectionRegistry, streamReaderFactory,
streamWriterFactory);
this.handshakeManager = handshakeManager;
this.contactExchangeManager = contactExchangeManager;
this.connectionManager = connectionManager;
this.pendingContactId = pendingContactId;
this.transportId = transportId;
this.connection = connection;
reader = connection.getReader();
writer = connection.getWriter();
}
@Nullable
StreamContext allocateStreamContext(PendingContactId pendingContactId,
TransportId transportId) {
try {
return keyManager.getStreamContext(pendingContactId, transportId);
} catch (DbException e) {
logException(LOG, WARNING, e);
return null;
}
}
void onError(boolean recognised) {
disposeOnError(reader, recognised);
disposeOnError(writer);
}
}

View File

@@ -1,107 +0,0 @@
package org.briarproject.bramble.connection;
import org.briarproject.bramble.api.connection.ConnectionRegistry;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import org.briarproject.bramble.api.properties.TransportPropertyManager;
import org.briarproject.bramble.api.sync.PriorityHandler;
import org.briarproject.bramble.api.sync.SyncSession;
import org.briarproject.bramble.api.sync.SyncSessionFactory;
import org.briarproject.bramble.api.transport.KeyManager;
import org.briarproject.bramble.api.transport.StreamContext;
import org.briarproject.bramble.api.transport.StreamReaderFactory;
import org.briarproject.bramble.api.transport.StreamWriterFactory;
import java.io.IOException;
import java.util.concurrent.Executor;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.util.LogUtils.logException;
@NotNullByDefault
class IncomingDuplexSyncConnection extends DuplexSyncConnection
implements Runnable {
IncomingDuplexSyncConnection(KeyManager keyManager,
ConnectionRegistry connectionRegistry,
StreamReaderFactory streamReaderFactory,
StreamWriterFactory streamWriterFactory,
SyncSessionFactory syncSessionFactory,
TransportPropertyManager transportPropertyManager,
Executor ioExecutor, TransportId transportId,
DuplexTransportConnection connection) {
super(keyManager, connectionRegistry, streamReaderFactory,
streamWriterFactory, syncSessionFactory,
transportPropertyManager, ioExecutor, transportId, connection);
}
@Override
public void run() {
// Read and recognise the tag
StreamContext ctx = recogniseTag(reader, transportId);
if (ctx == null) {
LOG.info("Unrecognised tag");
onReadError(false);
return;
}
ContactId contactId = ctx.getContactId();
if (contactId == null) {
LOG.warning("Expected contact tag, got rendezvous tag");
onReadError(true);
return;
}
if (ctx.isHandshakeMode()) {
// TODO: Support handshake mode for contacts
LOG.warning("Received handshake tag, expected rotation mode");
onReadError(true);
return;
}
connectionRegistry.registerIncomingConnection(contactId, transportId,
this);
// Start the outgoing session on another thread
ioExecutor.execute(() -> runOutgoingSession(contactId));
try {
// Store any transport properties discovered from the connection
transportPropertyManager.addRemotePropertiesFromConnection(
contactId, transportId, remote);
// Update the connection registry when we receive our priority
PriorityHandler handler = p -> connectionRegistry.setPriority(
contactId, transportId, this, p);
// Create and run the incoming session
createIncomingSession(ctx, reader, handler).run();
reader.dispose(false, true);
interruptOutgoingSession();
connectionRegistry.unregisterConnection(contactId, transportId,
this, true, false);
} catch (DbException | IOException e) {
logException(LOG, WARNING, e);
onReadError(true);
connectionRegistry.unregisterConnection(contactId, transportId,
this, true, true);
}
}
private void runOutgoingSession(ContactId contactId) {
// Allocate a stream context
StreamContext ctx = allocateStreamContext(contactId, transportId);
if (ctx == null) {
LOG.warning("Could not allocate stream context");
onWriteError();
return;
}
try {
// Create and run the outgoing session
SyncSession out = createDuplexOutgoingSession(ctx, writer, null);
setOutgoingSession(out);
out.run();
writer.dispose(false);
} catch (IOException e) {
logException(LOG, WARNING, e);
onWriteError();
}
}
}

View File

@@ -1,93 +0,0 @@
package org.briarproject.bramble.connection;
import org.briarproject.bramble.api.connection.ConnectionManager;
import org.briarproject.bramble.api.connection.ConnectionRegistry;
import org.briarproject.bramble.api.contact.ContactExchangeManager;
import org.briarproject.bramble.api.contact.HandshakeManager;
import org.briarproject.bramble.api.contact.HandshakeManager.HandshakeResult;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import org.briarproject.bramble.api.transport.KeyManager;
import org.briarproject.bramble.api.transport.StreamContext;
import org.briarproject.bramble.api.transport.StreamReaderFactory;
import org.briarproject.bramble.api.transport.StreamWriter;
import org.briarproject.bramble.api.transport.StreamWriterFactory;
import java.io.IOException;
import java.io.InputStream;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.util.LogUtils.logException;
@NotNullByDefault
class IncomingHandshakeConnection extends HandshakeConnection
implements Runnable {
IncomingHandshakeConnection(KeyManager keyManager,
ConnectionRegistry connectionRegistry,
StreamReaderFactory streamReaderFactory,
StreamWriterFactory streamWriterFactory,
HandshakeManager handshakeManager,
ContactExchangeManager contactExchangeManager,
ConnectionManager connectionManager,
PendingContactId pendingContactId,
TransportId transportId, DuplexTransportConnection connection) {
super(keyManager, connectionRegistry, streamReaderFactory,
streamWriterFactory, handshakeManager, contactExchangeManager,
connectionManager, pendingContactId, transportId, connection);
}
@Override
public void run() {
// Read and recognise the tag
StreamContext ctxIn = recogniseTag(reader, transportId);
if (ctxIn == null) {
LOG.info("Unrecognised tag");
onError(false);
return;
}
PendingContactId inPendingContactId = ctxIn.getPendingContactId();
if (inPendingContactId == null) {
LOG.warning("Expected rendezvous tag, got contact tag");
onError(true);
return;
}
// Allocate the outgoing stream context
StreamContext ctxOut =
allocateStreamContext(pendingContactId, transportId);
if (ctxOut == null) {
LOG.warning("Could not allocate stream context");
onError(true);
return;
}
// Close the connection if it's redundant
if (!connectionRegistry.registerConnection(pendingContactId)) {
LOG.info("Redundant rendezvous connection");
onError(true);
return;
}
// Handshake and exchange contacts
try {
InputStream in = streamReaderFactory.createStreamReader(
reader.getInputStream(), ctxIn);
// Flush the output stream to send the outgoing stream header
StreamWriter out = streamWriterFactory.createStreamWriter(
writer.getOutputStream(), ctxOut);
out.getOutputStream().flush();
HandshakeResult result =
handshakeManager.handshake(pendingContactId, in, out);
contactExchangeManager.exchangeContacts(pendingContactId,
connection, result.getMasterKey(), result.isAlice(), false);
connectionRegistry.unregisterConnection(pendingContactId, true);
// Reuse the connection as a transport connection
connectionManager.manageIncomingConnection(transportId, connection);
} catch (IOException | DbException e) {
logException(LOG, WARNING, e);
onError(true);
connectionRegistry.unregisterConnection(pendingContactId, false);
}
}
}

View File

@@ -1,79 +0,0 @@
package org.briarproject.bramble.connection;
import org.briarproject.bramble.api.connection.ConnectionRegistry;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportConnectionReader;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.properties.TransportPropertyManager;
import org.briarproject.bramble.api.sync.PriorityHandler;
import org.briarproject.bramble.api.sync.SyncSessionFactory;
import org.briarproject.bramble.api.transport.KeyManager;
import org.briarproject.bramble.api.transport.StreamContext;
import org.briarproject.bramble.api.transport.StreamReaderFactory;
import org.briarproject.bramble.api.transport.StreamWriterFactory;
import java.io.IOException;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.util.LogUtils.logException;
@NotNullByDefault
class IncomingSimplexSyncConnection extends SyncConnection implements Runnable {
private final TransportId transportId;
private final TransportConnectionReader reader;
IncomingSimplexSyncConnection(KeyManager keyManager,
ConnectionRegistry connectionRegistry,
StreamReaderFactory streamReaderFactory,
StreamWriterFactory streamWriterFactory,
SyncSessionFactory syncSessionFactory,
TransportPropertyManager transportPropertyManager,
TransportId transportId, TransportConnectionReader reader) {
super(keyManager, connectionRegistry, streamReaderFactory,
streamWriterFactory, syncSessionFactory,
transportPropertyManager);
this.transportId = transportId;
this.reader = reader;
}
@Override
public void run() {
// Read and recognise the tag
StreamContext ctx = recogniseTag(reader, transportId);
if (ctx == null) {
LOG.info("Unrecognised tag");
onError(false);
return;
}
ContactId contactId = ctx.getContactId();
if (contactId == null) {
LOG.warning("Received rendezvous stream, expected contact");
onError(true);
return;
}
if (ctx.isHandshakeMode()) {
// TODO: Support handshake mode for contacts
LOG.warning("Received handshake tag, expected rotation mode");
onError(true);
return;
}
try {
// We don't expect to receive a priority for this connection
PriorityHandler handler = p ->
LOG.info("Ignoring priority for simplex connection");
// Create and run the incoming session
createIncomingSession(ctx, reader, handler).run();
reader.dispose(false, true);
} catch (IOException e) {
logException(LOG, WARNING, e);
onError(true);
}
}
private void onError(boolean recognised) {
disposeOnError(reader, recognised);
}
}

View File

@@ -1,140 +0,0 @@
package org.briarproject.bramble.connection;
import org.briarproject.bramble.api.connection.ConnectionRegistry;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import org.briarproject.bramble.api.properties.TransportPropertyManager;
import org.briarproject.bramble.api.sync.Priority;
import org.briarproject.bramble.api.sync.PriorityHandler;
import org.briarproject.bramble.api.sync.SyncSession;
import org.briarproject.bramble.api.sync.SyncSessionFactory;
import org.briarproject.bramble.api.transport.KeyManager;
import org.briarproject.bramble.api.transport.StreamContext;
import org.briarproject.bramble.api.transport.StreamReaderFactory;
import org.briarproject.bramble.api.transport.StreamWriterFactory;
import java.io.IOException;
import java.security.SecureRandom;
import java.util.concurrent.Executor;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.api.sync.SyncConstants.PRIORITY_NONCE_BYTES;
import static org.briarproject.bramble.util.LogUtils.logException;
@NotNullByDefault
class OutgoingDuplexSyncConnection extends DuplexSyncConnection
implements Runnable {
private final SecureRandom secureRandom;
private final ContactId contactId;
OutgoingDuplexSyncConnection(KeyManager keyManager,
ConnectionRegistry connectionRegistry,
StreamReaderFactory streamReaderFactory,
StreamWriterFactory streamWriterFactory,
SyncSessionFactory syncSessionFactory,
TransportPropertyManager transportPropertyManager,
Executor ioExecutor, SecureRandom secureRandom, ContactId contactId,
TransportId transportId, DuplexTransportConnection connection) {
super(keyManager, connectionRegistry, streamReaderFactory,
streamWriterFactory, syncSessionFactory,
transportPropertyManager, ioExecutor, transportId, connection);
this.secureRandom = secureRandom;
this.contactId = contactId;
}
@Override
public void run() {
// Allocate a stream context
StreamContext ctx = allocateStreamContext(contactId, transportId);
if (ctx == null) {
LOG.warning("Could not allocate stream context");
onWriteError();
return;
}
if (ctx.isHandshakeMode()) {
// TODO: Support handshake mode for contacts
LOG.warning("Cannot use handshake mode stream context");
onWriteError();
return;
}
// Start the incoming session on another thread
Priority priority = generatePriority();
ioExecutor.execute(() -> runIncomingSession(priority));
try {
// Create and run the outgoing session
SyncSession out =
createDuplexOutgoingSession(ctx, writer, priority);
setOutgoingSession(out);
out.run();
writer.dispose(false);
} catch (IOException e) {
logException(LOG, WARNING, e);
onWriteError();
}
}
private void runIncomingSession(Priority priority) {
// Read and recognise the tag
StreamContext ctx = recogniseTag(reader, transportId);
// Unrecognised tags are suspicious in this case
if (ctx == null) {
LOG.warning("Unrecognised tag for returning stream");
onReadError();
return;
}
// Check that the stream comes from the expected contact
ContactId inContactId = ctx.getContactId();
if (inContactId == null) {
LOG.warning("Expected contact tag, got rendezvous tag");
onReadError();
return;
}
if (!contactId.equals(inContactId)) {
LOG.warning("Wrong contact ID for returning stream");
onReadError();
return;
}
if (ctx.isHandshakeMode()) {
// TODO: Support handshake mode for contacts
LOG.warning("Received handshake tag, expected rotation mode");
onReadError();
return;
}
connectionRegistry.registerOutgoingConnection(contactId, transportId,
this, priority);
try {
// Store any transport properties discovered from the connection
transportPropertyManager.addRemotePropertiesFromConnection(
contactId, transportId, remote);
// We don't expect to receive a priority for this connection
PriorityHandler handler = p ->
LOG.info("Ignoring priority for outgoing connection");
// Create and run the incoming session
createIncomingSession(ctx, reader, handler).run();
reader.dispose(false, true);
interruptOutgoingSession();
connectionRegistry.unregisterConnection(contactId, transportId,
this, false, false);
} catch (DbException | IOException e) {
logException(LOG, WARNING, e);
onReadError();
connectionRegistry.unregisterConnection(contactId, transportId,
this, false, true);
}
}
private void onReadError() {
// 'Recognised' is always true for outgoing connections
onReadError(true);
}
private Priority generatePriority() {
byte[] nonce = new byte[PRIORITY_NONCE_BYTES];
secureRandom.nextBytes(nonce);
return new Priority(nonce);
}
}

View File

@@ -1,115 +0,0 @@
package org.briarproject.bramble.connection;
import org.briarproject.bramble.api.connection.ConnectionManager;
import org.briarproject.bramble.api.connection.ConnectionRegistry;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactExchangeManager;
import org.briarproject.bramble.api.contact.HandshakeManager;
import org.briarproject.bramble.api.contact.HandshakeManager.HandshakeResult;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import org.briarproject.bramble.api.transport.KeyManager;
import org.briarproject.bramble.api.transport.StreamContext;
import org.briarproject.bramble.api.transport.StreamReaderFactory;
import org.briarproject.bramble.api.transport.StreamWriter;
import org.briarproject.bramble.api.transport.StreamWriterFactory;
import java.io.IOException;
import java.io.InputStream;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.util.LogUtils.logException;
@NotNullByDefault
class OutgoingHandshakeConnection extends HandshakeConnection
implements Runnable {
OutgoingHandshakeConnection(KeyManager keyManager,
ConnectionRegistry connectionRegistry,
StreamReaderFactory streamReaderFactory,
StreamWriterFactory streamWriterFactory,
HandshakeManager handshakeManager,
ContactExchangeManager contactExchangeManager,
ConnectionManager connectionManager,
PendingContactId pendingContactId,
TransportId transportId, DuplexTransportConnection connection) {
super(keyManager, connectionRegistry, streamReaderFactory,
streamWriterFactory, handshakeManager, contactExchangeManager,
connectionManager, pendingContactId, transportId, connection);
}
@Override
public void run() {
// Allocate the outgoing stream context
StreamContext ctxOut =
allocateStreamContext(pendingContactId, transportId);
if (ctxOut == null) {
LOG.warning("Could not allocate stream context");
onError();
return;
}
// Flush the output stream to send the outgoing stream header
StreamWriter out;
try {
out = streamWriterFactory.createStreamWriter(
writer.getOutputStream(), ctxOut);
out.getOutputStream().flush();
} catch (IOException e) {
logException(LOG, WARNING, e);
onError();
return;
}
// Read and recognise the tag
StreamContext ctxIn = recogniseTag(reader, transportId);
// Unrecognised tags are suspicious in this case
if (ctxIn == null) {
LOG.warning("Unrecognised tag for returning stream");
onError();
return;
}
// Check that the stream comes from the expected pending contact
PendingContactId inPendingContactId = ctxIn.getPendingContactId();
if (inPendingContactId == null) {
LOG.warning("Expected rendezvous tag, got contact tag");
onError();
return;
}
if (!inPendingContactId.equals(pendingContactId)) {
LOG.warning("Wrong pending contact ID for returning stream");
onError();
return;
}
// Close the connection if it's redundant
if (!connectionRegistry.registerConnection(pendingContactId)) {
LOG.info("Redundant rendezvous connection");
onError();
return;
}
// Handshake and exchange contacts
try {
InputStream in = streamReaderFactory.createStreamReader(
reader.getInputStream(), ctxIn);
HandshakeResult result =
handshakeManager.handshake(pendingContactId, in, out);
Contact contact = contactExchangeManager.exchangeContacts(
pendingContactId, connection, result.getMasterKey(),
result.isAlice(), false);
connectionRegistry.unregisterConnection(pendingContactId, true);
// Reuse the connection as a transport connection
connectionManager.manageOutgoingConnection(contact.getId(),
transportId, connection);
} catch (IOException | DbException e) {
logException(LOG, WARNING, e);
onError();
connectionRegistry.unregisterConnection(pendingContactId, false);
}
}
private void onError() {
// 'Recognised' is always true for outgoing connections
onError(true);
}
}

View File

@@ -1,78 +0,0 @@
package org.briarproject.bramble.connection;
import org.briarproject.bramble.api.connection.ConnectionRegistry;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.properties.TransportPropertyManager;
import org.briarproject.bramble.api.sync.SyncSession;
import org.briarproject.bramble.api.sync.SyncSessionFactory;
import org.briarproject.bramble.api.transport.KeyManager;
import org.briarproject.bramble.api.transport.StreamContext;
import org.briarproject.bramble.api.transport.StreamReaderFactory;
import org.briarproject.bramble.api.transport.StreamWriter;
import org.briarproject.bramble.api.transport.StreamWriterFactory;
import java.io.IOException;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
import static org.briarproject.bramble.util.LogUtils.logException;
@NotNullByDefault
class OutgoingSimplexSyncConnection extends SyncConnection implements Runnable {
private final ContactId contactId;
private final TransportId transportId;
private final TransportConnectionWriter writer;
OutgoingSimplexSyncConnection(KeyManager keyManager,
ConnectionRegistry connectionRegistry,
StreamReaderFactory streamReaderFactory,
StreamWriterFactory streamWriterFactory,
SyncSessionFactory syncSessionFactory,
TransportPropertyManager transportPropertyManager,
ContactId contactId, TransportId transportId,
TransportConnectionWriter writer) {
super(keyManager, connectionRegistry, streamReaderFactory,
streamWriterFactory, syncSessionFactory,
transportPropertyManager);
this.contactId = contactId;
this.transportId = transportId;
this.writer = writer;
}
@Override
public void run() {
// Allocate a stream context
StreamContext ctx = allocateStreamContext(contactId, transportId);
if (ctx == null) {
LOG.warning("Could not allocate stream context");
onError();
return;
}
try {
// Create and run the outgoing session
createSimplexOutgoingSession(ctx, writer).run();
writer.dispose(false);
} catch (IOException e) {
logException(LOG, WARNING, e);
onError();
}
}
private void onError() {
disposeOnError(writer);
}
private SyncSession createSimplexOutgoingSession(StreamContext ctx,
TransportConnectionWriter w) throws IOException {
StreamWriter streamWriter = streamWriterFactory.createStreamWriter(
w.getOutputStream(), ctx);
ContactId c = requireNonNull(ctx.getContactId());
return syncSessionFactory.createSimplexOutgoingSession(c,
ctx.getTransportId(), w.getMaxLatency(), streamWriter);
}
}

View File

@@ -1,64 +0,0 @@
package org.briarproject.bramble.connection;
import org.briarproject.bramble.api.connection.ConnectionRegistry;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportConnectionReader;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.properties.TransportPropertyManager;
import org.briarproject.bramble.api.sync.PriorityHandler;
import org.briarproject.bramble.api.sync.SyncSession;
import org.briarproject.bramble.api.sync.SyncSessionFactory;
import org.briarproject.bramble.api.transport.KeyManager;
import org.briarproject.bramble.api.transport.StreamContext;
import org.briarproject.bramble.api.transport.StreamReaderFactory;
import org.briarproject.bramble.api.transport.StreamWriterFactory;
import java.io.IOException;
import java.io.InputStream;
import javax.annotation.Nullable;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
import static org.briarproject.bramble.util.LogUtils.logException;
@NotNullByDefault
class SyncConnection extends Connection {
final SyncSessionFactory syncSessionFactory;
final TransportPropertyManager transportPropertyManager;
SyncConnection(KeyManager keyManager, ConnectionRegistry connectionRegistry,
StreamReaderFactory streamReaderFactory,
StreamWriterFactory streamWriterFactory,
SyncSessionFactory syncSessionFactory,
TransportPropertyManager transportPropertyManager) {
super(keyManager, connectionRegistry, streamReaderFactory,
streamWriterFactory);
this.syncSessionFactory = syncSessionFactory;
this.transportPropertyManager = transportPropertyManager;
}
@Nullable
StreamContext allocateStreamContext(ContactId contactId,
TransportId transportId) {
try {
return keyManager.getStreamContext(contactId, transportId);
} catch (DbException e) {
logException(LOG, WARNING, e);
return null;
}
}
SyncSession createIncomingSession(StreamContext ctx,
TransportConnectionReader r, PriorityHandler handler)
throws IOException {
InputStream streamReader = streamReaderFactory.createStreamReader(
r.getInputStream(), ctx);
ContactId c = requireNonNull(ctx.getContactId());
return syncSessionFactory
.createIncomingSession(c, streamReader, handler);
}
}

View File

@@ -0,0 +1,709 @@
package org.briarproject.bramble.plugin;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactExchangeManager;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.HandshakeManager;
import org.briarproject.bramble.api.contact.HandshakeManager.HandshakeResult;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.ConnectionManager;
import org.briarproject.bramble.api.plugin.ConnectionRegistry;
import org.briarproject.bramble.api.plugin.TransportConnectionReader;
import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.properties.TransportPropertyManager;
import org.briarproject.bramble.api.sync.SyncSession;
import org.briarproject.bramble.api.sync.SyncSessionFactory;
import org.briarproject.bramble.api.transport.KeyManager;
import org.briarproject.bramble.api.transport.StreamContext;
import org.briarproject.bramble.api.transport.StreamReaderFactory;
import org.briarproject.bramble.api.transport.StreamWriter;
import org.briarproject.bramble.api.transport.StreamWriterFactory;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.inject.Inject;
import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
import static org.briarproject.bramble.util.IoUtils.read;
import static org.briarproject.bramble.util.LogUtils.logException;
@NotNullByDefault
class ConnectionManagerImpl implements ConnectionManager {
private static final Logger LOG =
getLogger(ConnectionManagerImpl.class.getName());
private final Executor ioExecutor;
private final KeyManager keyManager;
private final StreamReaderFactory streamReaderFactory;
private final StreamWriterFactory streamWriterFactory;
private final SyncSessionFactory syncSessionFactory;
private final HandshakeManager handshakeManager;
private final ContactExchangeManager contactExchangeManager;
private final ConnectionRegistry connectionRegistry;
private final TransportPropertyManager transportPropertyManager;
@Inject
ConnectionManagerImpl(@IoExecutor Executor ioExecutor,
KeyManager keyManager, StreamReaderFactory streamReaderFactory,
StreamWriterFactory streamWriterFactory,
SyncSessionFactory syncSessionFactory,
HandshakeManager handshakeManager,
ContactExchangeManager contactExchangeManager,
ConnectionRegistry connectionRegistry,
TransportPropertyManager transportPropertyManager) {
this.ioExecutor = ioExecutor;
this.keyManager = keyManager;
this.streamReaderFactory = streamReaderFactory;
this.streamWriterFactory = streamWriterFactory;
this.syncSessionFactory = syncSessionFactory;
this.handshakeManager = handshakeManager;
this.contactExchangeManager = contactExchangeManager;
this.connectionRegistry = connectionRegistry;
this.transportPropertyManager = transportPropertyManager;
}
@Override
public void manageIncomingConnection(TransportId t,
TransportConnectionReader r) {
ioExecutor.execute(new ManageIncomingSimplexConnection(t, r));
}
@Override
public void manageIncomingConnection(TransportId t,
DuplexTransportConnection d) {
ioExecutor.execute(new ManageIncomingDuplexConnection(t, d));
}
@Override
public void manageIncomingConnection(PendingContactId p, TransportId t,
DuplexTransportConnection d) {
ioExecutor.execute(new ManageIncomingHandshakeConnection(p, t, d));
}
@Override
public void manageOutgoingConnection(ContactId c, TransportId t,
TransportConnectionWriter w) {
ioExecutor.execute(new ManageOutgoingSimplexConnection(c, t, w));
}
@Override
public void manageOutgoingConnection(ContactId c, TransportId t,
DuplexTransportConnection d) {
ioExecutor.execute(new ManageOutgoingDuplexConnection(c, t, d));
}
@Override
public void manageOutgoingConnection(PendingContactId p, TransportId t,
DuplexTransportConnection d) {
ioExecutor.execute(new ManageOutgoingHandshakeConnection(p, t, d));
}
private byte[] readTag(InputStream in) throws IOException {
byte[] tag = new byte[TAG_LENGTH];
read(in, tag);
return tag;
}
private SyncSession createIncomingSession(StreamContext ctx,
TransportConnectionReader r) throws IOException {
InputStream streamReader = streamReaderFactory.createStreamReader(
r.getInputStream(), ctx);
ContactId c = requireNonNull(ctx.getContactId());
return syncSessionFactory.createIncomingSession(c, streamReader);
}
private SyncSession createSimplexOutgoingSession(StreamContext ctx,
TransportConnectionWriter w) throws IOException {
StreamWriter streamWriter = streamWriterFactory.createStreamWriter(
w.getOutputStream(), ctx);
return syncSessionFactory.createSimplexOutgoingSession(
requireNonNull(ctx.getContactId()), ctx.getTransportId(),
w.getMaxLatency(), streamWriter);
}
private SyncSession createDuplexOutgoingSession(StreamContext ctx,
TransportConnectionWriter w) throws IOException {
StreamWriter streamWriter = streamWriterFactory.createStreamWriter(
w.getOutputStream(), ctx);
return syncSessionFactory.createDuplexOutgoingSession(
requireNonNull(ctx.getContactId()), ctx.getTransportId(),
w.getMaxLatency(), w.getMaxIdleTime(), streamWriter);
}
private void disposeOnError(TransportConnectionReader reader,
boolean recognised) {
try {
reader.dispose(true, recognised);
} catch (IOException e) {
logException(LOG, WARNING, e);
}
}
private void disposeOnError(TransportConnectionWriter writer) {
try {
writer.dispose(true);
} catch (IOException e) {
logException(LOG, WARNING, e);
}
}
private class ManageIncomingSimplexConnection implements Runnable {
private final TransportId transportId;
private final TransportConnectionReader reader;
private ManageIncomingSimplexConnection(TransportId transportId,
TransportConnectionReader reader) {
this.transportId = transportId;
this.reader = reader;
}
@Override
public void run() {
// Read and recognise the tag
StreamContext ctx;
try {
byte[] tag = readTag(reader.getInputStream());
ctx = keyManager.getStreamContext(transportId, tag);
} catch (IOException | DbException e) {
logException(LOG, WARNING, e);
onError(false);
return;
}
if (ctx == null) {
LOG.info("Unrecognised tag");
onError(false);
return;
}
ContactId contactId = ctx.getContactId();
if (contactId == null) {
LOG.warning("Received rendezvous stream, expected contact");
onError(true);
return;
}
if (ctx.isHandshakeMode()) {
// TODO: Support handshake mode for contacts
LOG.warning("Received handshake tag, expected rotation mode");
onError(true);
return;
}
connectionRegistry.registerConnection(contactId, transportId, true);
try {
// Create and run the incoming session
createIncomingSession(ctx, reader).run();
reader.dispose(false, true);
} catch (IOException e) {
logException(LOG, WARNING, e);
onError(true);
} finally {
connectionRegistry.unregisterConnection(contactId, transportId,
true);
}
}
private void onError(boolean recognised) {
disposeOnError(reader, recognised);
}
}
private class ManageOutgoingSimplexConnection implements Runnable {
private final ContactId contactId;
private final TransportId transportId;
private final TransportConnectionWriter writer;
private ManageOutgoingSimplexConnection(ContactId contactId,
TransportId transportId, TransportConnectionWriter writer) {
this.contactId = contactId;
this.transportId = transportId;
this.writer = writer;
}
@Override
public void run() {
// Allocate a stream context
StreamContext ctx;
try {
ctx = keyManager.getStreamContext(contactId, transportId);
} catch (DbException e) {
logException(LOG, WARNING, e);
onError();
return;
}
if (ctx == null) {
LOG.warning("Could not allocate stream context");
onError();
return;
}
connectionRegistry.registerConnection(contactId, transportId,
false);
try {
// Create and run the outgoing session
createSimplexOutgoingSession(ctx, writer).run();
writer.dispose(false);
} catch (IOException e) {
logException(LOG, WARNING, e);
onError();
} finally {
connectionRegistry.unregisterConnection(contactId, transportId,
false);
}
}
private void onError() {
disposeOnError(writer);
}
}
private class ManageIncomingDuplexConnection implements Runnable {
private final TransportId transportId;
private final TransportConnectionReader reader;
private final TransportConnectionWriter writer;
private final TransportProperties remote;
@Nullable
private volatile SyncSession outgoingSession = null;
private ManageIncomingDuplexConnection(TransportId transportId,
DuplexTransportConnection connection) {
this.transportId = transportId;
reader = connection.getReader();
writer = connection.getWriter();
remote = connection.getRemoteProperties();
}
@Override
public void run() {
// Read and recognise the tag
StreamContext ctx;
try {
byte[] tag = readTag(reader.getInputStream());
ctx = keyManager.getStreamContext(transportId, tag);
} catch (IOException | DbException e) {
logException(LOG, WARNING, e);
onReadError(false);
return;
}
if (ctx == null) {
LOG.info("Unrecognised tag");
onReadError(false);
return;
}
ContactId contactId = ctx.getContactId();
if (contactId == null) {
LOG.warning("Expected contact tag, got rendezvous tag");
onReadError(true);
return;
}
if (ctx.isHandshakeMode()) {
// TODO: Support handshake mode for contacts
LOG.warning("Received handshake tag, expected rotation mode");
onReadError(true);
return;
}
connectionRegistry.registerConnection(contactId, transportId, true);
// Start the outgoing session on another thread
ioExecutor.execute(() -> runOutgoingSession(contactId));
try {
// Store any transport properties discovered from the connection
transportPropertyManager.addRemotePropertiesFromConnection(
contactId, transportId, remote);
// Create and run the incoming session
createIncomingSession(ctx, reader).run();
reader.dispose(false, true);
// Interrupt the outgoing session so it finishes cleanly
SyncSession out = outgoingSession;
if (out != null) out.interrupt();
} catch (DbException | IOException e) {
logException(LOG, WARNING, e);
onReadError(true);
} finally {
connectionRegistry.unregisterConnection(contactId, transportId,
true);
}
}
private void runOutgoingSession(ContactId contactId) {
// Allocate a stream context
StreamContext ctx;
try {
ctx = keyManager.getStreamContext(contactId, transportId);
} catch (DbException e) {
logException(LOG, WARNING, e);
onWriteError();
return;
}
if (ctx == null) {
LOG.warning("Could not allocate stream context");
onWriteError();
return;
}
try {
// Create and run the outgoing session
SyncSession out = createDuplexOutgoingSession(ctx, writer);
outgoingSession = out;
out.run();
writer.dispose(false);
} catch (IOException e) {
logException(LOG, WARNING, e);
onWriteError();
}
}
private void onReadError(boolean recognised) {
disposeOnError(reader, recognised);
disposeOnError(writer);
// Interrupt the outgoing session so it finishes
SyncSession out = outgoingSession;
if (out != null) out.interrupt();
}
private void onWriteError() {
disposeOnError(reader, true);
disposeOnError(writer);
}
}
private class ManageOutgoingDuplexConnection implements Runnable {
private final ContactId contactId;
private final TransportId transportId;
private final TransportConnectionReader reader;
private final TransportConnectionWriter writer;
private final TransportProperties remote;
@Nullable
private volatile SyncSession outgoingSession = null;
private ManageOutgoingDuplexConnection(ContactId contactId,
TransportId transportId, DuplexTransportConnection connection) {
this.contactId = contactId;
this.transportId = transportId;
reader = connection.getReader();
writer = connection.getWriter();
remote = connection.getRemoteProperties();
}
@Override
public void run() {
// Allocate a stream context
StreamContext ctx;
try {
ctx = keyManager.getStreamContext(contactId, transportId);
} catch (DbException e) {
logException(LOG, WARNING, e);
onWriteError();
return;
}
if (ctx == null) {
LOG.warning("Could not allocate stream context");
onWriteError();
return;
}
if (ctx.isHandshakeMode()) {
// TODO: Support handshake mode for contacts
LOG.warning("Cannot use handshake mode stream context");
onWriteError();
return;
}
// Start the incoming session on another thread
ioExecutor.execute(this::runIncomingSession);
try {
// Create and run the outgoing session
SyncSession out = createDuplexOutgoingSession(ctx, writer);
outgoingSession = out;
out.run();
writer.dispose(false);
} catch (IOException e) {
logException(LOG, WARNING, e);
onWriteError();
}
}
private void runIncomingSession() {
// Read and recognise the tag
StreamContext ctx;
try {
byte[] tag = readTag(reader.getInputStream());
ctx = keyManager.getStreamContext(transportId, tag);
} catch (IOException | DbException e) {
logException(LOG, WARNING, e);
onReadError();
return;
}
// Unrecognised tags are suspicious in this case
if (ctx == null) {
LOG.warning("Unrecognised tag for returning stream");
onReadError();
return;
}
// Check that the stream comes from the expected contact
ContactId inContactId = ctx.getContactId();
if (inContactId == null) {
LOG.warning("Expected contact tag, got rendezvous tag");
onReadError();
return;
}
if (!contactId.equals(inContactId)) {
LOG.warning("Wrong contact ID for returning stream");
onReadError();
return;
}
if (ctx.isHandshakeMode()) {
// TODO: Support handshake mode for contacts
LOG.warning("Received handshake tag, expected rotation mode");
onReadError();
return;
}
connectionRegistry.registerConnection(contactId, transportId,
false);
try {
// Store any transport properties discovered from the connection
transportPropertyManager.addRemotePropertiesFromConnection(
contactId, transportId, remote);
// Create and run the incoming session
createIncomingSession(ctx, reader).run();
reader.dispose(false, true);
// Interrupt the outgoing session so it finishes cleanly
SyncSession out = outgoingSession;
if (out != null) out.interrupt();
} catch (DbException | IOException e) {
logException(LOG, WARNING, e);
onReadError();
} finally {
connectionRegistry.unregisterConnection(contactId, transportId,
false);
}
}
private void onReadError() {
// 'Recognised' is always true for outgoing connections
disposeOnError(reader, true);
disposeOnError(writer);
// Interrupt the outgoing session so it finishes
SyncSession out = outgoingSession;
if (out != null) out.interrupt();
}
private void onWriteError() {
disposeOnError(reader, true);
disposeOnError(writer);
}
}
private class ManageIncomingHandshakeConnection implements Runnable {
private final PendingContactId pendingContactId;
private final TransportId transportId;
private final DuplexTransportConnection connection;
private final TransportConnectionReader reader;
private final TransportConnectionWriter writer;
private ManageIncomingHandshakeConnection(
PendingContactId pendingContactId, TransportId transportId,
DuplexTransportConnection connection) {
this.pendingContactId = pendingContactId;
this.transportId = transportId;
this.connection = connection;
reader = connection.getReader();
writer = connection.getWriter();
}
@Override
public void run() {
// Read and recognise the tag
StreamContext ctxIn;
try {
byte[] tag = readTag(reader.getInputStream());
ctxIn = keyManager.getStreamContext(transportId, tag);
} catch (IOException | DbException e) {
logException(LOG, WARNING, e);
onError(false);
return;
}
if (ctxIn == null) {
LOG.info("Unrecognised tag");
onError(false);
return;
}
PendingContactId inPendingContactId = ctxIn.getPendingContactId();
if (inPendingContactId == null) {
LOG.warning("Expected rendezvous tag, got contact tag");
onError(true);
return;
}
// Allocate the outgoing stream context
StreamContext ctxOut;
try {
ctxOut = keyManager.getStreamContext(pendingContactId,
transportId);
} catch (DbException e) {
logException(LOG, WARNING, e);
onError(true);
return;
}
if (ctxOut == null) {
LOG.warning("Could not allocate stream context");
onError(true);
return;
}
// Close the connection if it's redundant
if (!connectionRegistry.registerConnection(pendingContactId)) {
LOG.info("Redundant rendezvous connection");
onError(true);
return;
}
// Handshake and exchange contacts
try {
InputStream in = streamReaderFactory.createStreamReader(
reader.getInputStream(), ctxIn);
// Flush the output stream to send the outgoing stream header
StreamWriter out = streamWriterFactory.createStreamWriter(
writer.getOutputStream(), ctxOut);
out.getOutputStream().flush();
HandshakeResult result = handshakeManager.handshake(
pendingContactId, in, out);
Contact contact = contactExchangeManager.exchangeContacts(
pendingContactId, connection, result.getMasterKey(),
result.isAlice(), false);
connectionRegistry.unregisterConnection(pendingContactId, true);
// Reuse the connection as a transport connection
manageOutgoingConnection(contact.getId(), transportId,
connection);
} catch (IOException | DbException e) {
logException(LOG, WARNING, e);
onError(true);
connectionRegistry.unregisterConnection(pendingContactId,
false);
}
}
private void onError(boolean recognised) {
disposeOnError(reader, recognised);
disposeOnError(writer);
}
}
private class ManageOutgoingHandshakeConnection implements Runnable {
private final PendingContactId pendingContactId;
private final TransportId transportId;
private final DuplexTransportConnection connection;
private final TransportConnectionReader reader;
private final TransportConnectionWriter writer;
private ManageOutgoingHandshakeConnection(
PendingContactId pendingContactId, TransportId transportId,
DuplexTransportConnection connection) {
this.pendingContactId = pendingContactId;
this.transportId = transportId;
this.connection = connection;
reader = connection.getReader();
writer = connection.getWriter();
}
@Override
public void run() {
// Allocate the outgoing stream context
StreamContext ctxOut;
try {
ctxOut = keyManager.getStreamContext(pendingContactId,
transportId);
} catch (DbException e) {
logException(LOG, WARNING, e);
onError();
return;
}
if (ctxOut == null) {
LOG.warning("Could not allocate stream context");
onError();
return;
}
// Flush the output stream to send the outgoing stream header
StreamWriter out;
try {
out = streamWriterFactory.createStreamWriter(
writer.getOutputStream(), ctxOut);
out.getOutputStream().flush();
} catch (IOException e) {
logException(LOG, WARNING, e);
onError();
return;
}
// Read and recognise the tag
StreamContext ctxIn;
try {
byte[] tag = readTag(reader.getInputStream());
ctxIn = keyManager.getStreamContext(transportId, tag);
} catch (IOException | DbException e) {
logException(LOG, WARNING, e);
onError();
return;
}
// Unrecognised tags are suspicious in this case
if (ctxIn == null) {
LOG.warning("Unrecognised tag for returning stream");
onError();
return;
}
// Check that the stream comes from the expected pending contact
PendingContactId inPendingContactId = ctxIn.getPendingContactId();
if (inPendingContactId == null) {
LOG.warning("Expected rendezvous tag, got contact tag");
onError();
return;
}
if (!inPendingContactId.equals(pendingContactId)) {
LOG.warning("Wrong pending contact ID for returning stream");
onError();
return;
}
// Close the connection if it's redundant
if (!connectionRegistry.registerConnection(pendingContactId)) {
LOG.info("Redundant rendezvous connection");
onError();
return;
}
// Handshake and exchange contacts
try {
InputStream in = streamReaderFactory.createStreamReader(
reader.getInputStream(), ctxIn);
HandshakeResult result = handshakeManager.handshake(
pendingContactId, in, out);
Contact contact = contactExchangeManager.exchangeContacts(
pendingContactId, connection, result.getMasterKey(),
result.isAlice(), false);
connectionRegistry.unregisterConnection(pendingContactId, true);
// Reuse the connection as a transport connection
manageOutgoingConnection(contact.getId(), transportId,
connection);
} catch (IOException | DbException e) {
logException(LOG, WARNING, e);
onError();
connectionRegistry.unregisterConnection(pendingContactId,
false);
}
}
private void onError() {
// 'Recognised' is always true for outgoing connections
disposeOnError(reader, true);
disposeOnError(writer);
}
}
}

View File

@@ -0,0 +1,150 @@
package org.briarproject.bramble.plugin;
import org.briarproject.bramble.api.Multiset;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.ConnectionRegistry;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.event.ConnectionClosedEvent;
import org.briarproject.bramble.api.plugin.event.ConnectionOpenedEvent;
import org.briarproject.bramble.api.plugin.event.ContactConnectedEvent;
import org.briarproject.bramble.api.plugin.event.ContactDisconnectedEvent;
import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionClosedEvent;
import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionOpenedEvent;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;
import javax.annotation.concurrent.GuardedBy;
import javax.annotation.concurrent.ThreadSafe;
import javax.inject.Inject;
import static java.util.logging.Level.INFO;
import static java.util.logging.Logger.getLogger;
@ThreadSafe
@NotNullByDefault
class ConnectionRegistryImpl implements ConnectionRegistry {
private static final Logger LOG =
getLogger(ConnectionRegistryImpl.class.getName());
private final EventBus eventBus;
private final Object lock = new Object();
@GuardedBy("lock")
private final Map<TransportId, Multiset<ContactId>> contactConnections;
@GuardedBy("lock")
private final Multiset<ContactId> contactCounts;
@GuardedBy("lock")
private final Set<PendingContactId> connectedPendingContacts;
@Inject
ConnectionRegistryImpl(EventBus eventBus) {
this.eventBus = eventBus;
contactConnections = new HashMap<>();
contactCounts = new Multiset<>();
connectedPendingContacts = new HashSet<>();
}
@Override
public void registerConnection(ContactId c, TransportId t,
boolean incoming) {
if (LOG.isLoggable(INFO)) {
if (incoming) LOG.info("Incoming connection registered: " + t);
else LOG.info("Outgoing connection registered: " + t);
}
boolean firstConnection = false;
synchronized (lock) {
Multiset<ContactId> m = contactConnections.get(t);
if (m == null) {
m = new Multiset<>();
contactConnections.put(t, m);
}
m.add(c);
if (contactCounts.add(c) == 1) firstConnection = true;
}
eventBus.broadcast(new ConnectionOpenedEvent(c, t, incoming));
if (firstConnection) {
LOG.info("Contact connected");
eventBus.broadcast(new ContactConnectedEvent(c));
}
}
@Override
public void unregisterConnection(ContactId c, TransportId t,
boolean incoming) {
if (LOG.isLoggable(INFO)) {
if (incoming) LOG.info("Incoming connection unregistered: " + t);
else LOG.info("Outgoing connection unregistered: " + t);
}
boolean lastConnection = false;
synchronized (lock) {
Multiset<ContactId> m = contactConnections.get(t);
if (m == null || !m.contains(c))
throw new IllegalArgumentException();
m.remove(c);
if (contactCounts.remove(c) == 0) lastConnection = true;
}
eventBus.broadcast(new ConnectionClosedEvent(c, t, incoming));
if (lastConnection) {
LOG.info("Contact disconnected");
eventBus.broadcast(new ContactDisconnectedEvent(c));
}
}
@Override
public Collection<ContactId> getConnectedContacts(TransportId t) {
synchronized (lock) {
Multiset<ContactId> m = contactConnections.get(t);
if (m == null) return Collections.emptyList();
List<ContactId> ids = new ArrayList<>(m.keySet());
if (LOG.isLoggable(INFO))
LOG.info(ids.size() + " contacts connected: " + t);
return ids;
}
}
@Override
public boolean isConnected(ContactId c, TransportId t) {
synchronized (lock) {
Multiset<ContactId> m = contactConnections.get(t);
return m != null && m.contains(c);
}
}
@Override
public boolean isConnected(ContactId c) {
synchronized (lock) {
return contactCounts.contains(c);
}
}
@Override
public boolean registerConnection(PendingContactId p) {
boolean added;
synchronized (lock) {
added = connectedPendingContacts.add(p);
}
if (added) eventBus.broadcast(new RendezvousConnectionOpenedEvent(p));
return added;
}
@Override
public void unregisterConnection(PendingContactId p, boolean success) {
synchronized (lock) {
if (!connectedPendingContacts.remove(p))
throw new IllegalArgumentException();
}
eventBus.broadcast(new RendezvousConnectionClosedEvent(p, success));
}
}

View File

@@ -1,12 +1,12 @@
package org.briarproject.bramble.plugin;
import org.briarproject.bramble.api.connection.ConnectionManager;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.lifecycle.Service;
import org.briarproject.bramble.api.lifecycle.ServiceException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.ConnectionManager;
import org.briarproject.bramble.api.plugin.Plugin;
import org.briarproject.bramble.api.plugin.PluginCallback;
import org.briarproject.bramble.api.plugin.PluginConfig;

View File

@@ -3,6 +3,8 @@ package org.briarproject.bramble.plugin;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.plugin.BackoffFactory;
import org.briarproject.bramble.api.plugin.ConnectionManager;
import org.briarproject.bramble.api.plugin.ConnectionRegistry;
import org.briarproject.bramble.api.plugin.PluginConfig;
import org.briarproject.bramble.api.plugin.PluginManager;
@@ -27,6 +29,20 @@ public class PluginModule {
return new BackoffFactoryImpl();
}
@Provides
@Singleton
ConnectionManager provideConnectionManager(
ConnectionManagerImpl connectionManager) {
return connectionManager;
}
@Provides
@Singleton
ConnectionRegistry provideConnectionRegistry(
ConnectionRegistryImpl connectionRegistry) {
return connectionRegistry;
}
@Provides
@Singleton
PluginManager providePluginManager(LifecycleManager lifecycleManager,

View File

@@ -1,8 +1,6 @@
package org.briarproject.bramble.plugin;
import org.briarproject.bramble.api.Pair;
import org.briarproject.bramble.api.connection.ConnectionManager;
import org.briarproject.bramble.api.connection.ConnectionRegistry;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.event.ContactAddedEvent;
import org.briarproject.bramble.api.db.DbException;
@@ -11,6 +9,8 @@ import org.briarproject.bramble.api.event.EventListener;
import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.ConnectionHandler;
import org.briarproject.bramble.api.plugin.ConnectionManager;
import org.briarproject.bramble.api.plugin.ConnectionRegistry;
import org.briarproject.bramble.api.plugin.Plugin;
import org.briarproject.bramble.api.plugin.PluginManager;
import org.briarproject.bramble.api.plugin.TransportConnectionReader;
@@ -98,8 +98,8 @@ class PollerImpl implements Poller, EventListener {
ConnectionClosedEvent c = (ConnectionClosedEvent) e;
// Reschedule polling, the polling interval may have decreased
reschedule(c.getTransportId());
// If an outgoing connection failed, try to reconnect
if (!c.isIncoming() && c.isException()) {
if (!c.isIncoming()) {
// Connect to the disconnected contact
connectToContact(c.getContactId(), c.getTransportId());
}
} else if (e instanceof ConnectionOpenedEvent) {
@@ -215,7 +215,7 @@ class PollerImpl implements Poller, EventListener {
Map<ContactId, TransportProperties> remote =
transportPropertyManager.getRemoteProperties(t);
Collection<ContactId> connected =
connectionRegistry.getConnectedOrBetterContacts(t);
connectionRegistry.getConnectedContacts(t);
Collection<Pair<TransportProperties, ConnectionHandler>>
properties = new ArrayList<>();
for (Entry<ContactId, TransportProperties> e : remote.entrySet()) {

View File

@@ -3,6 +3,8 @@ package org.briarproject.bramble.plugin.bluetooth;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import static java.util.concurrent.TimeUnit.DAYS;
import static java.util.concurrent.TimeUnit.MINUTES;
import static java.util.concurrent.TimeUnit.SECONDS;
@NotNullByDefault
@@ -13,6 +15,18 @@ interface BluetoothConnectionLimiter {
*/
long STABILITY_PERIOD_MS = SECONDS.toMillis(90);
/**
* The minimum interval between attempts to raise the connection limit.
* This is longer than {@link #STABILITY_PERIOD_MS} so we don't start
* another attempt before knowing the outcome of the last one.
*/
long MIN_ATTEMPT_INTERVAL_MS = MINUTES.toMillis(2);
/**
* The maximum interval between attempts to raise the connection limit.
*/
long MAX_ATTEMPT_INTERVAL_MS = DAYS.toMillis(2);
/**
* Informs the limiter that key agreement has started.
*/
@@ -30,17 +44,27 @@ interface BluetoothConnectionLimiter {
boolean canOpenContactConnection();
/**
* Informs the limiter that the given connection has been opened.
* Informs the limiter that a contact connection has been opened.
* <p/>
* Returns true if the connection is allowed.
*/
void connectionOpened(DuplexTransportConnection conn);
boolean contactConnectionOpened(DuplexTransportConnection conn,
boolean incoming);
/**
* Informs the limiter that a key agreement connection has been opened.
*/
void keyAgreementConnectionOpened(DuplexTransportConnection conn);
/**
* Informs the limiter that the given connection has been closed.
*
* @param exception True if the connection was closed due to an exception.
*/
void connectionClosed(DuplexTransportConnection conn);
void connectionClosed(DuplexTransportConnection conn, boolean exception);
/**
* Informs the limiter that all connections have been closed.
* Informs the limiter that the Bluetooth adapter has been disabled.
*/
void allConnectionsClosed();
void bluetoothDisabled();
}

View File

@@ -13,7 +13,9 @@ import java.util.logging.Logger;
import javax.annotation.concurrent.GuardedBy;
import javax.annotation.concurrent.ThreadSafe;
import javax.inject.Inject;
import static java.lang.Math.min;
import static java.util.logging.Level.INFO;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.ID;
@@ -34,8 +36,12 @@ class BluetoothConnectionLimiterImpl implements BluetoothConnectionLimiter {
@GuardedBy("lock")
private boolean keyAgreementInProgress = false;
@GuardedBy("lock")
private int connectionLimit = 2;
private int connectionLimit = 1;
@GuardedBy("lock")
private long timeOfLastAttempt = 0,
attemptInterval = MIN_ATTEMPT_INTERVAL_MS;
@Inject
BluetoothConnectionLimiterImpl(EventBus eventBus, Clock clock) {
this.eventBus = eventBus;
this.clock = clock;
@@ -62,82 +68,127 @@ class BluetoothConnectionLimiterImpl implements BluetoothConnectionLimiter {
public boolean canOpenContactConnection() {
synchronized (lock) {
if (keyAgreementInProgress) {
LOG.info("Can't open contact connection during key agreement");
LOG.info("Refusing contact connection during key agreement");
return false;
}
long now = clock.currentTimeMillis();
countStableConnections(now);
if (connections.size() < connectionLimit) {
LOG.info("Can open contact connection");
return true;
} else {
LOG.info("Can't open contact connection due to limit");
long now = clock.currentTimeMillis();
return isContactConnectionAllowedByLimit(now);
}
}
}
@Override
public boolean contactConnectionOpened(DuplexTransportConnection conn,
boolean incoming) {
synchronized (lock) {
if (keyAgreementInProgress) {
LOG.info("Refusing contact connection during key agreement");
return false;
} else {
long now = clock.currentTimeMillis();
if (incoming || isContactConnectionAllowedByLimit(now)) {
connections.add(new ConnectionRecord(conn, now));
if (!incoming && connections.size() > connectionLimit) {
LOG.info("Attempting to raise connection limit");
timeOfLastAttempt = now;
}
return true;
} else {
return false;
}
}
}
}
@Override
public void connectionOpened(DuplexTransportConnection conn) {
public void keyAgreementConnectionOpened(DuplexTransportConnection conn) {
synchronized (lock) {
long now = clock.currentTimeMillis();
countStableConnections(now);
connections.add(new ConnectionRecord(conn, now));
if (LOG.isLoggable(INFO)) {
LOG.info("Connection opened, " + connections.size() + " open");
}
LOG.info("Accepting key agreement connection");
connections.add(
new ConnectionRecord(conn, clock.currentTimeMillis()));
}
}
@Override
public void connectionClosed(DuplexTransportConnection conn) {
public void connectionClosed(DuplexTransportConnection conn,
boolean exception) {
synchronized (lock) {
countStableConnections(clock.currentTimeMillis());
Iterator<ConnectionRecord> it = connections.iterator();
while (it.hasNext()) {
if (it.next().conn == conn) {
if (it.next().connection == conn) {
long now = clock.currentTimeMillis();
if (exception) connectionFailed(now);
else considerRaisingConnectionLimit(now);
it.remove();
break;
}
}
if (LOG.isLoggable(INFO)) {
if (LOG.isLoggable(INFO))
LOG.info("Connection closed, " + connections.size() + " open");
}
}
}
@Override
public void allConnectionsClosed() {
public void bluetoothDisabled() {
synchronized (lock) {
long now = clock.currentTimeMillis();
countStableConnections(now);
LOG.info("Bluetooth disabled");
considerRaisingConnectionLimit(clock.currentTimeMillis());
connections.clear();
LOG.info("All connections closed");
}
}
@GuardedBy("lock")
private void countStableConnections(long now) {
private boolean isContactConnectionAllowedByLimit(long now) {
considerRaisingConnectionLimit(now);
if (connections.size() > connectionLimit) {
LOG.info("Refusing contact connection, above limit");
return false;
} else if (connections.size() < connectionLimit) {
LOG.info("Allowing contact connection, below limit");
return true;
} else if (now - timeOfLastAttempt >= attemptInterval) {
LOG.info("Allowing contact connection, at limit");
return true;
} else {
LOG.info("Refusing contact connection, at limit");
return false;
}
}
@GuardedBy("lock")
private void considerRaisingConnectionLimit(long now) {
int stable = 0;
for (ConnectionRecord rec : connections) {
if (now - rec.timeOpened >= STABILITY_PERIOD_MS) stable++;
}
if (stable > connectionLimit) {
LOG.info("Raising connection limit");
connectionLimit = stable;
if (LOG.isLoggable(INFO)) {
LOG.info("Raising connection limit to " + connectionLimit);
}
attemptInterval = MIN_ATTEMPT_INTERVAL_MS;
}
if (LOG.isLoggable(INFO)) {
LOG.info(stable + " connections are stable, limit is "
+ connectionLimit);
}
}
private static class ConnectionRecord {
@GuardedBy("lock")
private void connectionFailed(long now) {
if (connections.size() > connectionLimit &&
now - timeOfLastAttempt < STABILITY_PERIOD_MS) {
LOG.info("Connection failed above limit, increasing interval");
attemptInterval = min(attemptInterval * 2, MAX_ATTEMPT_INTERVAL_MS);
}
}
private final DuplexTransportConnection conn;
private static final class ConnectionRecord {
private final DuplexTransportConnection connection;
private final long timeOpened;
private ConnectionRecord(DuplexTransportConnection conn,
private ConnectionRecord(DuplexTransportConnection connection,
long timeOpened) {
this.conn = conn;
this.connection = connection;
this.timeOpened = timeOpened;
}
}

View File

@@ -130,7 +130,7 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
void onAdapterDisabled() {
LOG.info("Bluetooth disabled");
tryToClose(socket);
connectionLimiter.allConnectionsClosed();
connectionLimiter.bluetoothDisabled();
callback.transportDisabled();
}
@@ -232,13 +232,25 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
return;
}
LOG.info("Connection received");
connectionLimiter.connectionOpened(conn);
backoff.reset();
callback.handleConnection(conn);
if (connectionLimiter.contactConnectionOpened(conn, true)) {
backoff.reset();
callback.handleConnection(conn);
} else {
tryToClose(conn);
}
if (!running) return;
}
}
private void tryToClose(DuplexTransportConnection conn) {
try {
conn.getWriter().dispose(false);
conn.getReader().dispose(false, false);
} catch (IOException e) {
logException(LOG, WARNING, e);
}
}
@Override
public void stop() {
running = false;
@@ -326,8 +338,13 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
String uuid = p.get(PROP_UUID);
if (isNullOrEmpty(uuid)) return null;
DuplexTransportConnection conn = connect(address, uuid);
if (conn != null) connectionLimiter.connectionOpened(conn);
return conn;
if (conn == null) return null;
if (connectionLimiter.contactConnectionOpened(conn, false)) {
return conn;
} else {
tryToClose(conn);
return null;
}
}
@Override
@@ -383,7 +400,7 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
LOG.info("Connecting to key agreement UUID " + uuid);
conn = connect(address, uuid);
}
if (conn != null) connectionLimiter.connectionOpened(conn);
if (conn != null) connectionLimiter.keyAgreementConnectionOpened(conn);
return conn;
}
@@ -452,7 +469,7 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
public KeyAgreementConnection accept() throws IOException {
DuplexTransportConnection conn = acceptConnection(ss);
if (LOG.isLoggable(INFO)) LOG.info(ID + ": Incoming connection");
connectionLimiter.connectionOpened(conn);
connectionLimiter.keyAgreementConnectionOpened(conn);
return new KeyAgreementConnection(conn, ID);
}

View File

@@ -2,7 +2,6 @@ package org.briarproject.bramble.rendezvous;
import org.briarproject.bramble.PoliteExecutor;
import org.briarproject.bramble.api.Pair;
import org.briarproject.bramble.api.connection.ConnectionManager;
import org.briarproject.bramble.api.contact.PendingContact;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.contact.PendingContactState;
@@ -24,6 +23,7 @@ import org.briarproject.bramble.api.lifecycle.Service;
import org.briarproject.bramble.api.lifecycle.ServiceException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.ConnectionHandler;
import org.briarproject.bramble.api.plugin.ConnectionManager;
import org.briarproject.bramble.api.plugin.Plugin;
import org.briarproject.bramble.api.plugin.PluginManager;
import org.briarproject.bramble.api.plugin.TransportConnectionReader;

View File

@@ -15,7 +15,6 @@ import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.sync.Ack;
import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.Offer;
import org.briarproject.bramble.api.sync.Priority;
import org.briarproject.bramble.api.sync.Request;
import org.briarproject.bramble.api.sync.SyncRecordWriter;
import org.briarproject.bramble.api.sync.SyncSession;
@@ -38,7 +37,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.annotation.concurrent.ThreadSafe;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
@@ -79,8 +77,6 @@ class DuplexOutgoingSession implements SyncSession, EventListener {
private final int maxLatency, maxIdleTime;
private final StreamWriter streamWriter;
private final SyncRecordWriter recordWriter;
@Nullable
private final Priority priority;
private final BlockingQueue<ThrowingRunnable<IOException>> writerTasks;
private final AtomicBoolean generateAckQueued = new AtomicBoolean(false);
@@ -95,8 +91,7 @@ class DuplexOutgoingSession implements SyncSession, EventListener {
DuplexOutgoingSession(DatabaseComponent db, Executor dbExecutor,
EventBus eventBus, Clock clock, ContactId contactId,
TransportId transportId, int maxLatency, int maxIdleTime,
StreamWriter streamWriter, SyncRecordWriter recordWriter,
@Nullable Priority priority) {
StreamWriter streamWriter, SyncRecordWriter recordWriter) {
this.db = db;
this.dbExecutor = dbExecutor;
this.eventBus = eventBus;
@@ -107,7 +102,6 @@ class DuplexOutgoingSession implements SyncSession, EventListener {
this.maxIdleTime = maxIdleTime;
this.streamWriter = streamWriter;
this.recordWriter = recordWriter;
this.priority = priority;
writerTasks = new LinkedBlockingQueue<>();
}
@@ -118,8 +112,6 @@ class DuplexOutgoingSession implements SyncSession, EventListener {
try {
// Send our supported protocol versions
recordWriter.writeVersions(new Versions(SUPPORTED_VERSIONS));
// Send our connection priority, if this is an outgoing connection
if (priority != null) recordWriter.writePriority(priority);
// Start a query for each type of record
generateAck();
generateBatch();

View File

@@ -15,8 +15,6 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.Ack;
import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.Offer;
import org.briarproject.bramble.api.sync.Priority;
import org.briarproject.bramble.api.sync.PriorityHandler;
import org.briarproject.bramble.api.sync.Request;
import org.briarproject.bramble.api.sync.SyncRecordReader;
import org.briarproject.bramble.api.sync.SyncSession;
@@ -49,19 +47,17 @@ class IncomingSession implements SyncSession, EventListener {
private final EventBus eventBus;
private final ContactId contactId;
private final SyncRecordReader recordReader;
private final PriorityHandler priorityHandler;
private volatile boolean interrupted = false;
IncomingSession(DatabaseComponent db, Executor dbExecutor,
EventBus eventBus, ContactId contactId,
SyncRecordReader recordReader, PriorityHandler priorityHandler) {
SyncRecordReader recordReader) {
this.db = db;
this.dbExecutor = dbExecutor;
this.eventBus = eventBus;
this.contactId = contactId;
this.recordReader = recordReader;
this.priorityHandler = priorityHandler;
}
@IoExecutor
@@ -90,9 +86,6 @@ class IncomingSession implements SyncSession, EventListener {
} else if (recordReader.hasVersions()) {
Versions v = recordReader.readVersions();
dbExecutor.execute(new ReceiveVersions(v));
} else if (recordReader.hasPriority()) {
Priority p = recordReader.readPriority();
priorityHandler.handle(p);
} else {
// unknown records are ignored in RecordReader#eof()
throw new FormatException();

View File

@@ -11,7 +11,6 @@ import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageFactory;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.sync.Offer;
import org.briarproject.bramble.api.sync.Priority;
import org.briarproject.bramble.api.sync.Request;
import org.briarproject.bramble.api.sync.SyncRecordReader;
import org.briarproject.bramble.api.sync.Versions;
@@ -27,12 +26,10 @@ import javax.annotation.concurrent.NotThreadSafe;
import static org.briarproject.bramble.api.sync.RecordTypes.ACK;
import static org.briarproject.bramble.api.sync.RecordTypes.MESSAGE;
import static org.briarproject.bramble.api.sync.RecordTypes.OFFER;
import static org.briarproject.bramble.api.sync.RecordTypes.PRIORITY;
import static org.briarproject.bramble.api.sync.RecordTypes.REQUEST;
import static org.briarproject.bramble.api.sync.RecordTypes.VERSIONS;
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_SUPPORTED_VERSIONS;
import static org.briarproject.bramble.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH;
import static org.briarproject.bramble.api.sync.SyncConstants.PRIORITY_NONCE_BYTES;
import static org.briarproject.bramble.api.sync.SyncConstants.PROTOCOL_VERSION;
@NotThreadSafe
@@ -51,7 +48,7 @@ class SyncRecordReaderImpl implements SyncRecordReader {
private static boolean isKnownRecordType(byte type) {
return type == ACK || type == MESSAGE || type == OFFER ||
type == REQUEST || type == VERSIONS || type == PRIORITY;
type == REQUEST || type == VERSIONS;
}
private final MessageFactory messageFactory;
@@ -177,23 +174,4 @@ class SyncRecordReaderImpl implements SyncRecordReader {
nextRecord = null;
return supported;
}
@Override
public boolean hasPriority() throws IOException {
return !eof() && getNextRecordType() == PRIORITY;
}
@Override
public Priority readPriority() throws IOException {
if (!hasPriority()) throw new FormatException();
return new Priority(readNonce());
}
private byte[] readNonce() throws IOException {
if (nextRecord == null) throw new AssertionError();
byte[] payload = nextRecord.getPayload();
if (payload.length != PRIORITY_NONCE_BYTES) throw new FormatException();
nextRecord = null;
return payload;
}
}

View File

@@ -8,7 +8,6 @@ import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageFactory;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.sync.Offer;
import org.briarproject.bramble.api.sync.Priority;
import org.briarproject.bramble.api.sync.Request;
import org.briarproject.bramble.api.sync.SyncRecordWriter;
import org.briarproject.bramble.api.sync.Versions;
@@ -21,7 +20,6 @@ import javax.annotation.concurrent.NotThreadSafe;
import static org.briarproject.bramble.api.sync.RecordTypes.ACK;
import static org.briarproject.bramble.api.sync.RecordTypes.MESSAGE;
import static org.briarproject.bramble.api.sync.RecordTypes.OFFER;
import static org.briarproject.bramble.api.sync.RecordTypes.PRIORITY;
import static org.briarproject.bramble.api.sync.RecordTypes.REQUEST;
import static org.briarproject.bramble.api.sync.RecordTypes.VERSIONS;
import static org.briarproject.bramble.api.sync.SyncConstants.PROTOCOL_VERSION;
@@ -75,12 +73,6 @@ class SyncRecordWriterImpl implements SyncRecordWriter {
writeRecord(VERSIONS);
}
@Override
public void writePriority(Priority p) throws IOException {
writer.writeRecord(
new Record(PROTOCOL_VERSION, PRIORITY, p.getNonce()));
}
@Override
public void flush() throws IOException {
writer.flush();

View File

@@ -6,8 +6,6 @@ import org.briarproject.bramble.api.db.DatabaseExecutor;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.sync.Priority;
import org.briarproject.bramble.api.sync.PriorityHandler;
import org.briarproject.bramble.api.sync.SyncRecordReader;
import org.briarproject.bramble.api.sync.SyncRecordReaderFactory;
import org.briarproject.bramble.api.sync.SyncRecordWriter;
@@ -21,7 +19,6 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.util.concurrent.Executor;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
@@ -50,12 +47,10 @@ class SyncSessionFactoryImpl implements SyncSessionFactory {
}
@Override
public SyncSession createIncomingSession(ContactId c, InputStream in,
PriorityHandler handler) {
public SyncSession createIncomingSession(ContactId c, InputStream in) {
SyncRecordReader recordReader =
recordReaderFactory.createRecordReader(in);
return new IncomingSession(db, dbExecutor, eventBus, c, recordReader,
handler);
return new IncomingSession(db, dbExecutor, eventBus, c, recordReader);
}
@Override
@@ -69,13 +64,13 @@ class SyncSessionFactoryImpl implements SyncSessionFactory {
}
@Override
public SyncSession createDuplexOutgoingSession(ContactId c, TransportId t,
int maxLatency, int maxIdleTime, StreamWriter streamWriter,
@Nullable Priority priority) {
public SyncSession createDuplexOutgoingSession(ContactId c,
TransportId t, int maxLatency, int maxIdleTime,
StreamWriter streamWriter) {
OutputStream out = streamWriter.getOutputStream();
SyncRecordWriter recordWriter =
recordWriterFactory.createRecordWriter(out);
return new DuplexOutgoingSession(db, dbExecutor, eventBus, clock, c, t,
maxLatency, maxIdleTime, streamWriter, recordWriter, priority);
maxLatency, maxIdleTime, streamWriter, recordWriter);
}
}

View File

@@ -1,611 +0,0 @@
package org.briarproject.bramble.connection;
import org.briarproject.bramble.api.connection.ConnectionRegistry;
import org.briarproject.bramble.api.connection.InterruptibleConnection;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.plugin.PluginConfig;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.event.ConnectionClosedEvent;
import org.briarproject.bramble.api.plugin.event.ConnectionOpenedEvent;
import org.briarproject.bramble.api.plugin.event.ContactConnectedEvent;
import org.briarproject.bramble.api.plugin.event.ContactDisconnectedEvent;
import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionClosedEvent;
import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionOpenedEvent;
import org.briarproject.bramble.api.sync.Priority;
import org.briarproject.bramble.test.BrambleMockTestCase;
import org.jmock.Expectations;
import org.junit.Test;
import java.util.Collection;
import static java.util.Collections.emptyList;
import static java.util.Collections.emptyMap;
import static java.util.Collections.singletonList;
import static java.util.Collections.singletonMap;
import static org.briarproject.bramble.test.TestUtils.getContactId;
import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.briarproject.bramble.test.TestUtils.getTransportId;
import static org.briarproject.bramble.util.StringUtils.fromHexString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
public class ConnectionRegistryImplTest extends BrambleMockTestCase {
private final EventBus eventBus = context.mock(EventBus.class);
private final PluginConfig pluginConfig = context.mock(PluginConfig.class);
private final InterruptibleConnection conn1 =
context.mock(InterruptibleConnection.class, "conn1");
private final InterruptibleConnection conn2 =
context.mock(InterruptibleConnection.class, "conn2");
private final InterruptibleConnection conn3 =
context.mock(InterruptibleConnection.class, "conn3");
private final ContactId contactId1 = getContactId();
private final ContactId contactId2 = getContactId();
private final TransportId transportId1 = getTransportId();
private final TransportId transportId2 = getTransportId();
private final TransportId transportId3 = getTransportId();
private final PendingContactId pendingContactId =
new PendingContactId(getRandomId());
private final Priority low =
new Priority(fromHexString("00000000000000000000000000000000"));
private final Priority high =
new Priority(fromHexString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"));
@Test
public void testRegisterMultipleConnections() {
context.checking(new Expectations() {{
allowing(pluginConfig).getTransportPreferences();
will(returnValue(emptyMap()));
}});
ConnectionRegistry c =
new ConnectionRegistryImpl(eventBus, pluginConfig);
// The registry should be empty
assertEquals(emptyList(), c.getConnectedContacts(transportId1));
assertEquals(emptyList(), c.getConnectedOrBetterContacts(transportId1));
assertEquals(emptyList(), c.getConnectedContacts(transportId2));
assertEquals(emptyList(), c.getConnectedOrBetterContacts(transportId2));
assertEquals(emptyList(), c.getConnectedContacts(transportId3));
assertEquals(emptyList(), c.getConnectedOrBetterContacts(transportId3));
assertFalse(c.isConnected(contactId1));
assertFalse(c.isConnected(contactId1, transportId1));
assertFalse(c.isConnected(contactId1, transportId2));
assertFalse(c.isConnected(contactId1, transportId3));
// Check that a registered connection shows up - this should
// broadcast a ConnectionOpenedEvent and a ContactConnectedEvent
context.checking(new Expectations() {{
oneOf(eventBus).broadcast(with(any(ConnectionOpenedEvent.class)));
oneOf(eventBus).broadcast(with(any(ContactConnectedEvent.class)));
}});
c.registerIncomingConnection(contactId1, transportId1, conn1);
context.assertIsSatisfied();
assertEquals(singletonList(contactId1),
c.getConnectedContacts(transportId1));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId1));
assertTrue(c.isConnected(contactId1));
assertTrue(c.isConnected(contactId1, transportId1));
// Register another connection with the same contact and transport -
// this should broadcast a ConnectionOpenedEvent and lookup should be
// unaffected
context.checking(new Expectations() {{
oneOf(eventBus).broadcast(with(any(ConnectionOpenedEvent.class)));
}});
c.registerIncomingConnection(contactId1, transportId1, conn2);
context.assertIsSatisfied();
assertEquals(singletonList(contactId1),
c.getConnectedContacts(transportId1));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId1));
assertTrue(c.isConnected(contactId1));
assertTrue(c.isConnected(contactId1, transportId1));
// Unregister one of the connections - this should broadcast a
// ConnectionClosedEvent and lookup should be unaffected
context.checking(new Expectations() {{
oneOf(eventBus).broadcast(with(any(ConnectionClosedEvent.class)));
}});
c.unregisterConnection(contactId1, transportId1, conn1, true, false);
context.assertIsSatisfied();
assertEquals(singletonList(contactId1),
c.getConnectedContacts(transportId1));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId1));
assertTrue(c.isConnected(contactId1));
assertTrue(c.isConnected(contactId1, transportId1));
// Unregister the other connection - this should broadcast a
// ConnectionClosedEvent and a ContactDisconnectedEvent
context.checking(new Expectations() {{
oneOf(eventBus).broadcast(with(any(ConnectionClosedEvent.class)));
oneOf(eventBus).broadcast(with(any(
ContactDisconnectedEvent.class)));
}});
c.unregisterConnection(contactId1, transportId1, conn2, true, false);
context.assertIsSatisfied();
assertEquals(emptyList(), c.getConnectedContacts(transportId1));
assertEquals(emptyList(), c.getConnectedOrBetterContacts(transportId1));
assertFalse(c.isConnected(contactId1));
assertFalse(c.isConnected(contactId1, transportId1));
// Try to unregister the connection again - exception should be thrown
try {
c.unregisterConnection(contactId1, transportId1, conn2,
true, false);
fail();
} catch (IllegalArgumentException expected) {
// Expected
}
}
@Test
public void testRegisterMultipleContacts() {
context.checking(new Expectations() {{
allowing(pluginConfig).getTransportPreferences();
will(returnValue(emptyMap()));
}});
ConnectionRegistry c =
new ConnectionRegistryImpl(eventBus, pluginConfig);
// Register two contacts with one transport, then one of the contacts
// with a second transport - this should broadcast three
// ConnectionOpenedEvents and two ContactConnectedEvents
context.checking(new Expectations() {{
exactly(3).of(eventBus).broadcast(with(any(
ConnectionOpenedEvent.class)));
exactly(2).of(eventBus).broadcast(with(any(
ContactConnectedEvent.class)));
}});
c.registerIncomingConnection(contactId1, transportId1, conn1);
c.registerIncomingConnection(contactId2, transportId1, conn2);
c.registerIncomingConnection(contactId2, transportId2, conn3);
context.assertIsSatisfied();
assertTrue(c.isConnected(contactId1));
assertTrue(c.isConnected(contactId2));
assertTrue(c.isConnected(contactId1, transportId1));
assertFalse(c.isConnected(contactId1, transportId2));
assertTrue(c.isConnected(contactId2, transportId1));
assertTrue(c.isConnected(contactId2, transportId2));
Collection<ContactId> connected = c.getConnectedContacts(transportId1);
assertEquals(2, connected.size());
assertTrue(connected.contains(contactId1));
assertTrue(connected.contains(contactId2));
connected = c.getConnectedOrBetterContacts(transportId1);
assertEquals(2, connected.size());
assertTrue(connected.contains(contactId1));
assertTrue(connected.contains(contactId2));
assertEquals(singletonList(contactId2),
c.getConnectedContacts(transportId2));
assertEquals(singletonList(contactId2),
c.getConnectedOrBetterContacts(transportId2));
}
@Test
public void testConnectionsAreNotInterruptedUnlessPriorityIsSet() {
// Prefer transport 2 to transport 1
context.checking(new Expectations() {{
allowing(pluginConfig).getTransportPreferences();
will(returnValue(
singletonMap(transportId1, singletonList(transportId2))));
}});
ConnectionRegistry c =
new ConnectionRegistryImpl(eventBus, pluginConfig);
// Connect via transport 1 (worse than 2) with no priority set
context.checking(new Expectations() {{
oneOf(eventBus).broadcast(with(any(ConnectionOpenedEvent.class)));
oneOf(eventBus).broadcast(with(any(ContactConnectedEvent.class)));
}});
c.registerIncomingConnection(contactId1, transportId1, conn1);
context.assertIsSatisfied();
assertEquals(singletonList(contactId1),
c.getConnectedContacts(transportId1));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId1));
assertEquals(emptyList(), c.getConnectedContacts(transportId2));
assertEquals(emptyList(), c.getConnectedOrBetterContacts(transportId2));
// Connect via transport 2 (better than 1) and set priority to high -
// the old connection should not be interrupted, despite using a worse
// transport, to remain compatible with old peers
context.checking(new Expectations() {{
oneOf(eventBus).broadcast(with(any(ConnectionOpenedEvent.class)));
}});
c.registerOutgoingConnection(contactId1, transportId2, conn2, high);
context.assertIsSatisfied();
assertEquals(singletonList(contactId1),
c.getConnectedContacts(transportId1));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId1));
assertEquals(singletonList(contactId1),
c.getConnectedContacts(transportId2));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId2));
// Connect via transport 3 (no preference) and set priority to high -
// again, no interruptions are expected
context.checking(new Expectations() {{
oneOf(eventBus).broadcast(with(any(ConnectionOpenedEvent.class)));
}});
c.registerOutgoingConnection(contactId1, transportId3, conn3, high);
context.assertIsSatisfied();
assertEquals(singletonList(contactId1),
c.getConnectedContacts(transportId1));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId1));
assertEquals(singletonList(contactId1),
c.getConnectedContacts(transportId2));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId2));
assertEquals(singletonList(contactId1),
c.getConnectedContacts(transportId3));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId3));
}
@Test
public void testNewConnectionIsInterruptedIfOldConnectionUsesBetterTransport() {
// Prefer transport 1 to transport 2
context.checking(new Expectations() {{
allowing(pluginConfig).getTransportPreferences();
will(returnValue(
singletonMap(transportId2, singletonList(transportId1))));
}});
ConnectionRegistry c =
new ConnectionRegistryImpl(eventBus, pluginConfig);
// Connect via transport 1 (better than 2) and set priority to low
context.checking(new Expectations() {{
oneOf(eventBus).broadcast(with(any(ConnectionOpenedEvent.class)));
oneOf(eventBus).broadcast(with(any(ContactConnectedEvent.class)));
}});
c.registerOutgoingConnection(contactId1, transportId1, conn1, low);
context.assertIsSatisfied();
assertEquals(singletonList(contactId1),
c.getConnectedContacts(transportId1));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId1));
// The contact is not connected via transport 2 but is connected via a
// better transport
assertEquals(emptyList(), c.getConnectedContacts(transportId2));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId2));
// Connect via transport 2 (worse than 1) and set priority to high -
// the new connection should be interrupted because it uses a worse
// transport
context.checking(new Expectations() {{
oneOf(conn2).interruptOutgoingSession();
oneOf(eventBus).broadcast(with(any(ConnectionOpenedEvent.class)));
}});
c.registerOutgoingConnection(contactId1, transportId2, conn2, high);
context.assertIsSatisfied();
assertEquals(singletonList(contactId1),
c.getConnectedContacts(transportId1));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId1));
assertEquals(singletonList(contactId1),
c.getConnectedContacts(transportId2));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId2));
// Connect via transport 3 (no preference) and set priority to low -
// no further interruptions
context.checking(new Expectations() {{
oneOf(eventBus).broadcast(with(any(ConnectionOpenedEvent.class)));
}});
c.registerOutgoingConnection(contactId1, transportId3, conn3, low);
context.assertIsSatisfied();
assertEquals(singletonList(contactId1),
c.getConnectedContacts(transportId1));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId1));
assertEquals(singletonList(contactId1),
c.getConnectedContacts(transportId2));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId2));
assertEquals(singletonList(contactId1),
c.getConnectedContacts(transportId3));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId3));
// Unregister the interrupted connection (transport 2)
context.checking(new Expectations() {{
oneOf(eventBus).broadcast(with(any(ConnectionClosedEvent.class)));
}});
c.unregisterConnection(contactId1, transportId2, conn2, true, false);
context.assertIsSatisfied();
assertEquals(singletonList(contactId1),
c.getConnectedContacts(transportId1));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId1));
// The contact is not connected via transport 2 but is connected via a
// better transport
assertEquals(emptyList(), c.getConnectedContacts(transportId2));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId2));
assertEquals(singletonList(contactId1),
c.getConnectedContacts(transportId3));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId3));
}
@Test
public void testOldConnectionIsInterruptedIfNewConnectionUsesBetterTransport() {
// Prefer transport 2 to transport 1
context.checking(new Expectations() {{
allowing(pluginConfig).getTransportPreferences();
will(returnValue(
singletonMap(transportId1, singletonList(transportId2))));
}});
ConnectionRegistry c =
new ConnectionRegistryImpl(eventBus, pluginConfig);
// Connect via transport 1 (worse than 2) and set priority to high
context.checking(new Expectations() {{
oneOf(eventBus).broadcast(with(any(ConnectionOpenedEvent.class)));
oneOf(eventBus).broadcast(with(any(ContactConnectedEvent.class)));
}});
c.registerOutgoingConnection(contactId1, transportId1, conn1, high);
context.assertIsSatisfied();
assertEquals(singletonList(contactId1),
c.getConnectedContacts(transportId1));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId1));
assertEquals(emptyList(), c.getConnectedContacts(transportId2));
assertEquals(emptyList(), c.getConnectedOrBetterContacts(transportId2));
// Connect via transport 2 (better than 1) and set priority to low -
// the old connection should be interrupted because it uses a worse
// transport
context.checking(new Expectations() {{
oneOf(conn1).interruptOutgoingSession();
oneOf(eventBus).broadcast(with(any(ConnectionOpenedEvent.class)));
}});
c.registerOutgoingConnection(contactId1, transportId2, conn2, low);
context.assertIsSatisfied();
assertEquals(singletonList(contactId1),
c.getConnectedContacts(transportId1));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId1));
assertEquals(singletonList(contactId1),
c.getConnectedContacts(transportId2));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId2));
// Connect via transport 3 (no preference) and set priority to high -
// no further interruptions
context.checking(new Expectations() {{
oneOf(eventBus).broadcast(with(any(ConnectionOpenedEvent.class)));
}});
c.registerOutgoingConnection(contactId1, transportId3, conn3, high);
context.assertIsSatisfied();
assertEquals(singletonList(contactId1),
c.getConnectedContacts(transportId1));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId1));
assertEquals(singletonList(contactId1),
c.getConnectedContacts(transportId2));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId2));
assertEquals(singletonList(contactId1),
c.getConnectedContacts(transportId3));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId3));
// Unregister the interrupted connection (transport 1)
context.checking(new Expectations() {{
oneOf(eventBus).broadcast(with(any(ConnectionClosedEvent.class)));
}});
c.unregisterConnection(contactId1, transportId1, conn1, true, false);
context.assertIsSatisfied();
// The contact is not connected via transport 1 but is connected via a
// better transport
assertEquals(emptyList(), c.getConnectedContacts(transportId1));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId1));
assertEquals(singletonList(contactId1),
c.getConnectedContacts(transportId2));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId2));
assertEquals(singletonList(contactId1),
c.getConnectedContacts(transportId3));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId3));
}
@Test
public void testNewConnectionIsInterruptedIfOldConnectionHasHigherPriority() {
context.checking(new Expectations() {{
allowing(pluginConfig).getTransportPreferences();
will(returnValue(emptyMap()));
}});
ConnectionRegistry c =
new ConnectionRegistryImpl(eventBus, pluginConfig);
// Register a connection with high priority
context.checking(new Expectations() {{
oneOf(eventBus).broadcast(with(any(ConnectionOpenedEvent.class)));
oneOf(eventBus).broadcast(with(any(ContactConnectedEvent.class)));
}});
c.registerOutgoingConnection(contactId1, transportId1, conn1, high);
context.assertIsSatisfied();
assertEquals(singletonList(contactId1),
c.getConnectedContacts(transportId1));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId1));
// Register another connection via the same transport (no priority yet)
context.checking(new Expectations() {{
oneOf(eventBus).broadcast(with(any(ConnectionOpenedEvent.class)));
}});
c.registerIncomingConnection(contactId1, transportId1, conn2);
context.assertIsSatisfied();
assertEquals(singletonList(contactId1),
c.getConnectedContacts(transportId1));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId1));
// Set the priority of the second connection to low - the second
// connection should be interrupted
context.checking(new Expectations() {{
oneOf(conn2).interruptOutgoingSession();
}});
c.setPriority(contactId1, transportId1, conn2, low);
context.assertIsSatisfied();
assertEquals(singletonList(contactId1),
c.getConnectedContacts(transportId1));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId1));
// Register a third connection with low priority - it should also be
// interrupted
context.checking(new Expectations() {{
oneOf(conn3).interruptOutgoingSession();
oneOf(eventBus).broadcast(with(any(ConnectionOpenedEvent.class)));
}});
c.registerOutgoingConnection(contactId1, transportId1, conn3, low);
context.assertIsSatisfied();
assertEquals(singletonList(contactId1),
c.getConnectedContacts(transportId1));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId1));
}
@Test
public void testOldConnectionIsInterruptedIfNewConnectionHasHigherPriority() {
context.checking(new Expectations() {{
allowing(pluginConfig).getTransportPreferences();
will(returnValue(emptyMap()));
}});
ConnectionRegistry c =
new ConnectionRegistryImpl(eventBus, pluginConfig);
// Register a connection with low priority
context.checking(new Expectations() {{
oneOf(eventBus).broadcast(with(any(ConnectionOpenedEvent.class)));
oneOf(eventBus).broadcast(with(any(ContactConnectedEvent.class)));
}});
c.registerOutgoingConnection(contactId1, transportId1, conn1, low);
context.assertIsSatisfied();
assertEquals(singletonList(contactId1),
c.getConnectedContacts(transportId1));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId1));
// Register another connection via the same transport (no priority yet)
context.checking(new Expectations() {{
oneOf(eventBus).broadcast(with(any(ConnectionOpenedEvent.class)));
}});
c.registerIncomingConnection(contactId1, transportId1, conn2);
context.assertIsSatisfied();
assertEquals(singletonList(contactId1),
c.getConnectedContacts(transportId1));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId1));
// Set the priority of the second connection to high - the first
// connection should be interrupted
context.checking(new Expectations() {{
oneOf(conn1).interruptOutgoingSession();
}});
c.setPriority(contactId1, transportId1, conn2, high);
context.assertIsSatisfied();
assertEquals(singletonList(contactId1),
c.getConnectedContacts(transportId1));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId1));
}
@Test
public void testRegisterAndUnregisterPendingContacts() {
context.checking(new Expectations() {{
allowing(pluginConfig).getTransportPreferences();
will(returnValue(emptyMap()));
}});
ConnectionRegistry c =
new ConnectionRegistryImpl(eventBus, pluginConfig);
context.checking(new Expectations() {{
oneOf(eventBus).broadcast(with(any(
RendezvousConnectionOpenedEvent.class)));
}});
assertTrue(c.registerConnection(pendingContactId));
assertFalse(c.registerConnection(pendingContactId)); // Redundant
context.assertIsSatisfied();
context.checking(new Expectations() {{
oneOf(eventBus).broadcast(with(any(
RendezvousConnectionClosedEvent.class)));
}});
c.unregisterConnection(pendingContactId, true);
context.assertIsSatisfied();
try {
c.unregisterConnection(pendingContactId, true);
fail();
} catch (IllegalArgumentException expected) {
// Expected
}
}
}

View File

@@ -2,13 +2,13 @@ package org.briarproject.bramble.contact;
import org.briarproject.bramble.BrambleCoreEagerSingletons;
import org.briarproject.bramble.BrambleCoreModule;
import org.briarproject.bramble.api.connection.ConnectionManager;
import org.briarproject.bramble.api.contact.ContactExchangeManager;
import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.plugin.ConnectionManager;
import org.briarproject.bramble.test.BrambleCoreIntegrationTestModule;
import java.util.concurrent.Executor;

View File

@@ -0,0 +1,149 @@
package org.briarproject.bramble.plugin;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.plugin.ConnectionRegistry;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.event.ConnectionClosedEvent;
import org.briarproject.bramble.api.plugin.event.ConnectionOpenedEvent;
import org.briarproject.bramble.api.plugin.event.ContactConnectedEvent;
import org.briarproject.bramble.api.plugin.event.ContactDisconnectedEvent;
import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionClosedEvent;
import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionOpenedEvent;
import org.briarproject.bramble.test.BrambleMockTestCase;
import org.jmock.Expectations;
import org.junit.Test;
import java.util.Collection;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static org.briarproject.bramble.test.TestUtils.getContactId;
import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.briarproject.bramble.test.TestUtils.getTransportId;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
public class ConnectionRegistryImplTest extends BrambleMockTestCase {
private final EventBus eventBus = context.mock(EventBus.class);
private final ContactId contactId = getContactId();
private final ContactId contactId1 = getContactId();
private final TransportId transportId = getTransportId();
private final TransportId transportId1 = getTransportId();
private final PendingContactId pendingContactId =
new PendingContactId(getRandomId());
@Test
public void testRegisterAndUnregister() {
ConnectionRegistry c = new ConnectionRegistryImpl(eventBus);
// The registry should be empty
assertEquals(emptyList(), c.getConnectedContacts(transportId));
assertEquals(emptyList(), c.getConnectedContacts(transportId1));
// Check that a registered connection shows up - this should
// broadcast a ConnectionOpenedEvent and a ContactConnectedEvent
context.checking(new Expectations() {{
oneOf(eventBus).broadcast(with(any(ConnectionOpenedEvent.class)));
oneOf(eventBus).broadcast(with(any(ContactConnectedEvent.class)));
}});
c.registerConnection(contactId, transportId, true);
assertEquals(singletonList(contactId),
c.getConnectedContacts(transportId));
assertEquals(emptyList(), c.getConnectedContacts(transportId1));
context.assertIsSatisfied();
// Register an identical connection - this should broadcast a
// ConnectionOpenedEvent and lookup should be unaffected
context.checking(new Expectations() {{
oneOf(eventBus).broadcast(with(any(ConnectionOpenedEvent.class)));
}});
c.registerConnection(contactId, transportId, true);
assertEquals(singletonList(contactId),
c.getConnectedContacts(transportId));
assertEquals(emptyList(), c.getConnectedContacts(transportId1));
context.assertIsSatisfied();
// Unregister one of the connections - this should broadcast a
// ConnectionClosedEvent and lookup should be unaffected
context.checking(new Expectations() {{
oneOf(eventBus).broadcast(with(any(ConnectionClosedEvent.class)));
}});
c.unregisterConnection(contactId, transportId, true);
assertEquals(singletonList(contactId),
c.getConnectedContacts(transportId));
assertEquals(emptyList(), c.getConnectedContacts(transportId1));
context.assertIsSatisfied();
// Unregister the other connection - this should broadcast a
// ConnectionClosedEvent and a ContactDisconnectedEvent
context.checking(new Expectations() {{
oneOf(eventBus).broadcast(with(any(ConnectionClosedEvent.class)));
oneOf(eventBus).broadcast(with(any(
ContactDisconnectedEvent.class)));
}});
c.unregisterConnection(contactId, transportId, true);
assertEquals(emptyList(), c.getConnectedContacts(transportId));
assertEquals(emptyList(), c.getConnectedContacts(transportId1));
context.assertIsSatisfied();
// Try to unregister the connection again - exception should be thrown
try {
c.unregisterConnection(contactId, transportId, true);
fail();
} catch (IllegalArgumentException expected) {
// Expected
}
// Register both contacts with one transport, one contact with both -
// this should broadcast three ConnectionOpenedEvents and two
// ContactConnectedEvents
context.checking(new Expectations() {{
exactly(3).of(eventBus).broadcast(with(any(
ConnectionOpenedEvent.class)));
exactly(2).of(eventBus).broadcast(with(any(
ContactConnectedEvent.class)));
}});
c.registerConnection(contactId, transportId, true);
c.registerConnection(contactId1, transportId, true);
c.registerConnection(contactId1, transportId1, true);
Collection<ContactId> connected = c.getConnectedContacts(transportId);
assertEquals(2, connected.size());
assertTrue(connected.contains(contactId));
assertTrue(connected.contains(contactId1));
assertEquals(singletonList(contactId1),
c.getConnectedContacts(transportId1));
}
@Test
public void testRegisterAndUnregisterPendingContacts() {
ConnectionRegistry c = new ConnectionRegistryImpl(eventBus);
context.checking(new Expectations() {{
oneOf(eventBus).broadcast(with(any(
RendezvousConnectionOpenedEvent.class)));
}});
assertTrue(c.registerConnection(pendingContactId));
assertFalse(c.registerConnection(pendingContactId)); // Redundant
context.assertIsSatisfied();
context.checking(new Expectations() {{
oneOf(eventBus).broadcast(with(any(
RendezvousConnectionClosedEvent.class)));
}});
c.unregisterConnection(pendingContactId, true);
context.assertIsSatisfied();
try {
c.unregisterConnection(pendingContactId, true);
fail();
} catch (IllegalArgumentException expected) {
// Expected
}
}
}

View File

@@ -1,7 +1,7 @@
package org.briarproject.bramble.plugin;
import org.briarproject.bramble.api.connection.ConnectionManager;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.plugin.ConnectionManager;
import org.briarproject.bramble.api.plugin.PluginCallback;
import org.briarproject.bramble.api.plugin.PluginConfig;
import org.briarproject.bramble.api.plugin.PluginException;

View File

@@ -1,10 +1,10 @@
package org.briarproject.bramble.plugin;
import org.briarproject.bramble.api.connection.ConnectionManager;
import org.briarproject.bramble.api.connection.ConnectionRegistry;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.event.ContactAddedEvent;
import org.briarproject.bramble.api.plugin.ConnectionHandler;
import org.briarproject.bramble.api.plugin.ConnectionManager;
import org.briarproject.bramble.api.plugin.ConnectionRegistry;
import org.briarproject.bramble.api.plugin.Plugin;
import org.briarproject.bramble.api.plugin.PluginManager;
import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
@@ -157,21 +157,7 @@ public class PollerImplTest extends BrambleMockTestCase {
}
@Test
public void testRescheduleOnOutgoingConnectionClosed() {
DuplexPlugin plugin = context.mock(DuplexPlugin.class);
context.checking(new Expectations() {{
allowing(plugin).getId();
will(returnValue(transportId));
}});
expectReschedule(plugin);
poller.eventOccurred(new ConnectionClosedEvent(contactId, transportId,
false, false));
}
@Test
public void testRescheduleAndReconnectOnOutgoingConnectionFailed()
public void testRescheduleAndReconnectOnConnectionClosed()
throws Exception {
DuplexPlugin plugin = context.mock(DuplexPlugin.class);
DuplexTransportConnection duplexConnection =
@@ -180,40 +166,45 @@ public class PollerImplTest extends BrambleMockTestCase {
context.checking(new Expectations() {{
allowing(plugin).getId();
will(returnValue(transportId));
// reschedule()
// Get the plugin
oneOf(pluginManager).getPlugin(transportId);
will(returnValue(plugin));
// The plugin supports polling
oneOf(plugin).shouldPoll();
will(returnValue(true));
// Get the plugin
oneOf(pluginManager).getPlugin(transportId);
will(returnValue(plugin));
// The plugin supports polling
oneOf(plugin).shouldPoll();
will(returnValue(true));
// Schedule the next poll
oneOf(plugin).getPollingInterval();
will(returnValue(pollingInterval));
oneOf(clock).currentTimeMillis();
will(returnValue(now));
oneOf(scheduler).schedule(with(any(Runnable.class)),
with((long) pollingInterval), with(MILLISECONDS));
will(returnValue(future));
// connectToContact()
// Check whether the contact is already connected
oneOf(connectionRegistry).isConnected(contactId, transportId);
will(returnValue(false));
// Get the transport properties
oneOf(transportPropertyManager).getRemoteProperties(contactId,
transportId);
will(returnValue(properties));
// Connect to the contact
oneOf(plugin).createConnection(properties);
will(returnValue(duplexConnection));
// Pass the connection to the connection manager
oneOf(connectionManager).manageOutgoingConnection(contactId,
transportId, duplexConnection);
}});
expectReschedule(plugin);
expectReconnect(plugin, duplexConnection);
poller.eventOccurred(new ConnectionClosedEvent(contactId, transportId,
false, true));
}
@Test
public void testRescheduleOnIncomingConnectionClosed() {
DuplexPlugin plugin = context.mock(DuplexPlugin.class);
context.checking(new Expectations() {{
allowing(plugin).getId();
will(returnValue(transportId));
}});
expectReschedule(plugin);
poller.eventOccurred(new ConnectionClosedEvent(contactId, transportId,
true, false));
}
@Test
public void testRescheduleOnIncomingConnectionFailed() {
DuplexPlugin plugin = context.mock(DuplexPlugin.class);
context.checking(new Expectations() {{
allowing(plugin).getId();
will(returnValue(transportId));
}});
expectReschedule(plugin);
poller.eventOccurred(new ConnectionClosedEvent(contactId, transportId,
true, false));
false));
}
@Test
@@ -363,7 +354,7 @@ public class PollerImplTest extends BrambleMockTestCase {
// Get the transport properties and connected contacts
oneOf(transportPropertyManager).getRemoteProperties(transportId);
will(returnValue(singletonMap(contactId, properties)));
oneOf(connectionRegistry).getConnectedOrBetterContacts(transportId);
oneOf(connectionRegistry).getConnectedContacts(transportId);
will(returnValue(emptyList()));
// Poll the plugin
oneOf(plugin).poll(with(collectionOf(
@@ -406,7 +397,7 @@ public class PollerImplTest extends BrambleMockTestCase {
// Get the transport properties and connected contacts
oneOf(transportPropertyManager).getRemoteProperties(transportId);
will(returnValue(singletonMap(contactId, properties)));
oneOf(connectionRegistry).getConnectedOrBetterContacts(transportId);
oneOf(connectionRegistry).getConnectedContacts(transportId);
will(returnValue(singletonList(contactId)));
// All contacts are connected, so don't poll the plugin
}});
@@ -440,48 +431,4 @@ public class PollerImplTest extends BrambleMockTestCase {
poller.eventOccurred(new TransportEnabledEvent(transportId));
poller.eventOccurred(new TransportDisabledEvent(transportId));
}
private void expectReschedule(Plugin plugin) {
context.checking(new Expectations() {{
// Get the plugin
oneOf(pluginManager).getPlugin(transportId);
will(returnValue(plugin));
// The plugin supports polling
oneOf(plugin).shouldPoll();
will(returnValue(true));
// Schedule the next poll
oneOf(plugin).getPollingInterval();
will(returnValue(pollingInterval));
oneOf(clock).currentTimeMillis();
will(returnValue(now));
oneOf(scheduler).schedule(with(any(Runnable.class)),
with((long) pollingInterval), with(MILLISECONDS));
will(returnValue(future));
}});
}
private void expectReconnect(DuplexPlugin plugin,
DuplexTransportConnection duplexConnection) throws Exception {
context.checking(new Expectations() {{
// Get the plugin
oneOf(pluginManager).getPlugin(transportId);
will(returnValue(plugin));
// The plugin supports polling
oneOf(plugin).shouldPoll();
will(returnValue(true));
// Check whether the contact is already connected
oneOf(connectionRegistry).isConnected(contactId, transportId);
will(returnValue(false));
// Get the transport properties
oneOf(transportPropertyManager).getRemoteProperties(contactId,
transportId);
will(returnValue(properties));
// Connect to the contact
oneOf(plugin).createConnection(properties);
will(returnValue(duplexConnection));
// Pass the connection to the connection manager
oneOf(connectionManager).manageOutgoingConnection(contactId,
transportId, duplexConnection);
}});
}
}

View File

@@ -0,0 +1,182 @@
package org.briarproject.bramble.plugin.bluetooth;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import org.briarproject.bramble.api.sync.event.CloseSyncConnectionsEvent;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.test.BrambleMockTestCase;
import org.briarproject.bramble.test.SettableClock;
import org.jmock.Expectations;
import org.junit.Before;
import org.junit.Test;
import java.util.concurrent.atomic.AtomicLong;
import static org.briarproject.bramble.plugin.bluetooth.BluetoothConnectionLimiter.MIN_ATTEMPT_INTERVAL_MS;
import static org.briarproject.bramble.plugin.bluetooth.BluetoothConnectionLimiter.STABILITY_PERIOD_MS;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
public class BluetoothConnectionLimiterImplTest extends BrambleMockTestCase {
private final EventBus eventBus = context.mock(EventBus.class);
private final DuplexTransportConnection conn1 =
context.mock(DuplexTransportConnection.class, "conn1");
private final DuplexTransportConnection conn2 =
context.mock(DuplexTransportConnection.class, "conn2");
private final DuplexTransportConnection conn3 =
context.mock(DuplexTransportConnection.class, "conn3");
private final long now = System.currentTimeMillis();
private AtomicLong time;
private BluetoothConnectionLimiter limiter;
@Before
public void setUp() {
time = new AtomicLong(now);
Clock clock = new SettableClock(time);
limiter = new BluetoothConnectionLimiterImpl(eventBus, clock);
}
@Test
public void testLimiterDoesNotAllowContactConnectionsDuringKeyAgreement() {
assertTrue(limiter.canOpenContactConnection());
expectCloseSyncConnectionsEvent();
limiter.keyAgreementStarted();
assertFalse(limiter.canOpenContactConnection());
limiter.keyAgreementEnded();
assertTrue(limiter.canOpenContactConnection());
}
@Test
public void testLimiterAllowsAttemptToRaiseLimitAtStartup() {
// First outgoing connection is allowed - we're below the limit of 1
assertTrue(limiter.canOpenContactConnection());
assertTrue(limiter.contactConnectionOpened(conn1, false));
// Second outgoing connection is allowed - it's time to try raising
// the limit to 2
assertTrue(limiter.canOpenContactConnection());
assertTrue(limiter.contactConnectionOpened(conn2, false));
// Third outgoing connection is not allowed - we're above the limit of 1
assertFalse(limiter.canOpenContactConnection());
}
@Test
public void testLimiterAllowsThirdConnectionAfterFirstTwoAreClosed() {
// First outgoing connection is allowed - we're below the limit of 1
assertTrue(limiter.canOpenContactConnection());
assertTrue(limiter.contactConnectionOpened(conn1, false));
// Second outgoing connection is allowed - it's time to try raising
// the limit to 2
assertTrue(limiter.canOpenContactConnection());
assertTrue(limiter.contactConnectionOpened(conn2, false));
// Third outgoing connection is not allowed - we're above the limit of 1
assertFalse(limiter.canOpenContactConnection());
// Close the first connection
limiter.connectionClosed(conn1, false);
// Third outgoing connection is not allowed - we're at the limit of 1
assertFalse(limiter.canOpenContactConnection());
// Close the second connection
limiter.connectionClosed(conn2, false);
// Third outgoing connection is allowed - we're below the limit of 1
assertTrue(limiter.canOpenContactConnection());
assertTrue(limiter.contactConnectionOpened(conn3, false));
}
@Test
public void testLimiterRaisesLimitWhenConnectionsAreStable() {
// First outgoing connection is allowed - we're below the limit of 1
assertTrue(limiter.canOpenContactConnection());
assertTrue(limiter.contactConnectionOpened(conn1, false));
// Second outgoing connection is allowed - it's time to try raising
// the limit to 2
assertTrue(limiter.canOpenContactConnection());
assertTrue(limiter.contactConnectionOpened(conn2, false));
// Third outgoing connection is not allowed - we're above the limit of 1
assertFalse(limiter.canOpenContactConnection());
// Time passes
time.set(now + STABILITY_PERIOD_MS);
// Third outgoing connection is still not allowed - first two are now
// stable so limit is raised to 2, but we're already at the new limit
assertFalse(limiter.canOpenContactConnection());
// Time passes
time.set(now + MIN_ATTEMPT_INTERVAL_MS);
// Third outgoing connection is allowed - it's time to try raising
// the limit to 3
assertTrue(limiter.canOpenContactConnection());
assertTrue(limiter.contactConnectionOpened(conn3, false));
// Fourth outgoing connection is not allowed - we're above the limit
// of 2
assertFalse(limiter.canOpenContactConnection());
}
@Test
public void testLimiterIncreasesIntervalWhenConnectionFailsAboveLimit() {
// First outgoing connection is allowed - we're below the limit of 1
assertTrue(limiter.canOpenContactConnection());
assertTrue(limiter.contactConnectionOpened(conn1, false));
// Time passes
time.set(now + 1);
// Second outgoing connection is allowed - it's time to try raising
// the limit to 2
assertTrue(limiter.canOpenContactConnection());
assertTrue(limiter.contactConnectionOpened(conn2, false));
// Time passes - the first connection is stable, the second isn't
time.set(now + STABILITY_PERIOD_MS);
// First connection fails. The second connection isn't stable yet, so
// the limiter considers this a failed attempt and doubles the interval
// between attempts
limiter.connectionClosed(conn1, true);
// Third outgoing connection is not allowed - we're still at the limit
// of 1
assertFalse(limiter.canOpenContactConnection());
// Time passes - nearly time for the second attempt
time.set(now + MIN_ATTEMPT_INTERVAL_MS * 2);
// Third outgoing connection is not allowed - we're still at the limit
// of 1
assertFalse(limiter.canOpenContactConnection());
// Time passes - now it's time for the second attempt
time.set(now + 1 + MIN_ATTEMPT_INTERVAL_MS * 2);
// Third outgoing connection is allowed - it's time to try raising the
// limit to 2 again
assertTrue(limiter.canOpenContactConnection());
assertTrue(limiter.contactConnectionOpened(conn3, false));
}
private void expectCloseSyncConnectionsEvent() {
context.checking(new Expectations() {{
oneOf(eventBus).broadcast(with(any(
CloseSyncConnectionsEvent.class)));
}});
}
}

View File

@@ -1,6 +1,5 @@
package org.briarproject.bramble.rendezvous;
import org.briarproject.bramble.api.connection.ConnectionManager;
import org.briarproject.bramble.api.contact.PendingContact;
import org.briarproject.bramble.api.contact.PendingContactState;
import org.briarproject.bramble.api.contact.event.PendingContactAddedEvent;
@@ -14,6 +13,7 @@ import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.plugin.ConnectionHandler;
import org.briarproject.bramble.api.plugin.ConnectionManager;
import org.briarproject.bramble.api.plugin.PluginManager;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;

View File

@@ -8,7 +8,6 @@ import org.briarproject.bramble.api.record.RecordReader;
import org.briarproject.bramble.api.sync.Ack;
import org.briarproject.bramble.api.sync.MessageFactory;
import org.briarproject.bramble.api.sync.Offer;
import org.briarproject.bramble.api.sync.Priority;
import org.briarproject.bramble.api.sync.Request;
import org.briarproject.bramble.api.sync.SyncRecordReader;
import org.briarproject.bramble.api.sync.Versions;
@@ -25,14 +24,11 @@ import javax.annotation.Nullable;
import static org.briarproject.bramble.api.record.Record.MAX_RECORD_PAYLOAD_BYTES;
import static org.briarproject.bramble.api.sync.RecordTypes.ACK;
import static org.briarproject.bramble.api.sync.RecordTypes.OFFER;
import static org.briarproject.bramble.api.sync.RecordTypes.PRIORITY;
import static org.briarproject.bramble.api.sync.RecordTypes.REQUEST;
import static org.briarproject.bramble.api.sync.RecordTypes.VERSIONS;
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_IDS;
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_SUPPORTED_VERSIONS;
import static org.briarproject.bramble.api.sync.SyncConstants.PRIORITY_NONCE_BYTES;
import static org.briarproject.bramble.api.sync.SyncConstants.PROTOCOL_VERSION;
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -123,31 +119,6 @@ public class SyncRecordReaderImplTest extends BrambleMockTestCase {
reader.readVersions();
}
@Test(expected = FormatException.class)
public void testFormatExceptionIfPriorityNonceIsTooSmall()
throws Exception {
expectReadRecord(createPriority(PRIORITY_NONCE_BYTES - 1));
reader.readPriority();
}
@Test(expected = FormatException.class)
public void testFormatExceptionIfPriorityNonceIsTooLarge()
throws Exception {
expectReadRecord(createPriority(PRIORITY_NONCE_BYTES + 1));
reader.readPriority();
}
@Test
public void testNoFormatExceptionIfPriorityNonceIsCorrectSize()
throws Exception {
expectReadRecord(createPriority(PRIORITY_NONCE_BYTES));
Priority priority = reader.readPriority();
assertEquals(PRIORITY_NONCE_BYTES, priority.getNonce().length);
}
@Test
public void testEofReturnsTrueWhenAtEndOfStream() throws Exception {
expectReadRecord(createAck());
@@ -202,11 +173,6 @@ public class SyncRecordReaderImplTest extends BrambleMockTestCase {
return new Record(PROTOCOL_VERSION, VERSIONS, payload);
}
private Record createPriority(int nonceBytes) {
byte[] payload = getRandomBytes(nonceBytes);
return new Record(PROTOCOL_VERSION, PRIORITY, payload);
}
private byte[] createPayload() throws Exception {
ByteArrayOutputStream payload = new ByteArrayOutputStream();
while (payload.size() + UniqueId.LENGTH <= MAX_RECORD_PAYLOAD_BYTES) {

View File

@@ -10,15 +10,12 @@ import org.briarproject.bramble.api.plugin.simplex.SimplexPlugin;
import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
import dagger.Module;
import dagger.Provides;
import static java.util.Collections.emptyMap;
import static java.util.Collections.singletonList;
import static org.briarproject.bramble.test.TestUtils.getTransportId;
@@ -88,12 +85,6 @@ public class TestPluginConfigModule {
public boolean shouldPoll() {
return false;
}
@Override
public Map<TransportId, List<TransportId>> getTransportPreferences() {
return emptyMap();
}
};
return pluginConfig;
}

View File

@@ -6,10 +6,7 @@ import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.lifecycle.ShutdownManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.BackoffFactory;
import org.briarproject.bramble.api.plugin.BluetoothConstants;
import org.briarproject.bramble.api.plugin.LanTcpConstants;
import org.briarproject.bramble.api.plugin.PluginConfig;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory;
import org.briarproject.bramble.api.reliability.ReliabilityLayerFactory;
@@ -21,8 +18,6 @@ import org.briarproject.bramble.plugin.tcp.WanTcpPluginFactory;
import java.security.SecureRandom;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
import dagger.Module;
@@ -30,8 +25,6 @@ import dagger.Provides;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static java.util.Collections.singletonMap;
@Module
public class DesktopPluginModule extends PluginModule {
@@ -70,13 +63,6 @@ public class DesktopPluginModule extends PluginModule {
public boolean shouldPoll() {
return true;
}
@Override
public Map<TransportId, List<TransportId>> getTransportPreferences() {
// Prefer LAN to Bluetooth
return singletonMap(BluetoothConstants.ID,
singletonList(LanTcpConstants.ID));
}
};
return pluginConfig;
}

View File

@@ -45,7 +45,7 @@ class JavaBluetoothTransportConnection
try {
stream.close();
} finally {
connectionLimiter.connectionClosed(this);
connectionLimiter.connectionClosed(this, exception);
}
}
}

View File

@@ -3,7 +3,7 @@ package org.briarproject.briar.android;
import android.app.Activity;
import android.util.Log;
import org.briarproject.bramble.api.connection.ConnectionRegistry;
import org.briarproject.bramble.api.plugin.ConnectionRegistry;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.briar.api.test.TestDataCreator;
import org.junit.ClassRule;

View File

@@ -120,8 +120,7 @@ public class SetupDataTest extends ScreenshotTest {
// TODO add messages
connectionRegistry.registerIncomingConnection(bob.getId(), ID, () -> {
});
connectionRegistry.registerConnection(bob.getId(), ID, true);
}
}

View File

@@ -7,7 +7,6 @@ import org.briarproject.bramble.BrambleCoreModule;
import org.briarproject.bramble.account.BriarAccountModule;
import org.briarproject.bramble.api.FeatureFlags;
import org.briarproject.bramble.api.account.AccountManager;
import org.briarproject.bramble.api.connection.ConnectionRegistry;
import org.briarproject.bramble.api.contact.ContactExchangeManager;
import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.crypto.CryptoExecutor;
@@ -20,6 +19,7 @@ import org.briarproject.bramble.api.keyagreement.PayloadEncoder;
import org.briarproject.bramble.api.keyagreement.PayloadParser;
import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.plugin.ConnectionRegistry;
import org.briarproject.bramble.api.plugin.PluginManager;
import org.briarproject.bramble.api.settings.SettingsManager;
import org.briarproject.bramble.api.system.AndroidExecutor;

View File

@@ -20,10 +20,7 @@ import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.network.NetworkManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.BackoffFactory;
import org.briarproject.bramble.api.plugin.BluetoothConstants;
import org.briarproject.bramble.api.plugin.LanTcpConstants;
import org.briarproject.bramble.api.plugin.PluginConfig;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory;
import org.briarproject.bramble.api.reporting.DevConfig;
@@ -51,8 +48,6 @@ import java.io.File;
import java.security.GeneralSecurityException;
import java.security.SecureRandom;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
@@ -67,8 +62,6 @@ import static android.content.Context.MODE_PRIVATE;
import static android.os.Build.VERSION.SDK_INT;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static java.util.Collections.singletonMap;
import static org.briarproject.bramble.api.reporting.ReportingConstants.DEV_ONION_ADDRESS;
import static org.briarproject.bramble.api.reporting.ReportingConstants.DEV_PUBLIC_KEY_HEX;
import static org.briarproject.briar.android.TestingConstants.IS_DEBUG_BUILD;
@@ -160,13 +153,6 @@ public class AppModule {
public boolean shouldPoll() {
return true;
}
@Override
public Map<TransportId, List<TransportId>> getTransportPreferences() {
// Prefer LAN to Bluetooth
return singletonMap(BluetoothConstants.ID,
singletonList(LanTcpConstants.ID));
}
};
return pluginConfig;
}

View File

@@ -10,7 +10,6 @@ import android.widget.TextView;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.snackbar.Snackbar;
import org.briarproject.bramble.api.connection.ConnectionRegistry;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.ContactManager;
@@ -25,6 +24,7 @@ import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.event.EventListener;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.plugin.ConnectionRegistry;
import org.briarproject.bramble.api.plugin.event.ContactConnectedEvent;
import org.briarproject.bramble.api.plugin.event.ContactDisconnectedEvent;
import org.briarproject.briar.R;

View File

@@ -1,11 +1,11 @@
package org.briarproject.briar.android.controller;
import org.briarproject.bramble.api.connection.ConnectionRegistry;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.event.EventListener;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.ConnectionRegistry;
import org.briarproject.bramble.api.plugin.event.ContactConnectedEvent;
import org.briarproject.bramble.api.plugin.event.ContactDisconnectedEvent;

View File

@@ -21,7 +21,6 @@ import com.google.android.material.snackbar.Snackbar;
import org.briarproject.bramble.api.FeatureFlags;
import org.briarproject.bramble.api.Pair;
import org.briarproject.bramble.api.connection.ConnectionRegistry;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.contact.event.ContactRemovedEvent;
@@ -34,6 +33,7 @@ import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.event.EventListener;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.plugin.ConnectionRegistry;
import org.briarproject.bramble.api.plugin.event.ContactConnectedEvent;
import org.briarproject.bramble.api.plugin.event.ContactDisconnectedEvent;
import org.briarproject.bramble.api.sync.ClientId;

View File

@@ -5,13 +5,13 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import org.briarproject.bramble.api.connection.ConnectionRegistry;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.plugin.ConnectionRegistry;
import org.briarproject.briar.R;
import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.contact.BaseContactListAdapter.OnContactClickListener;

View File

@@ -2,7 +2,6 @@ package org.briarproject.briar.android.keyagreement;
import android.app.Application;
import org.briarproject.bramble.api.connection.ConnectionManager;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactExchangeManager;
import org.briarproject.bramble.api.crypto.SecretKey;
@@ -11,6 +10,7 @@ import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.ConnectionManager;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;

View File

@@ -1,10 +1,10 @@
package org.briarproject.briar.android.privategroup.memberlist;
import org.briarproject.bramble.api.connection.ConnectionRegistry;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.db.DatabaseExecutor;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.plugin.ConnectionRegistry;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.briar.android.controller.DbControllerImpl;
import org.briarproject.briar.android.controller.handler.ResultExceptionHandler;

View File

@@ -5,7 +5,6 @@ import android.os.Bundle;
import android.view.MenuItem;
import android.widget.TextView;
import org.briarproject.bramble.api.connection.ConnectionRegistry;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.db.DatabaseExecutor;
import org.briarproject.bramble.api.db.DbException;
@@ -14,6 +13,7 @@ import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.event.EventListener;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.plugin.ConnectionRegistry;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.event.GroupRemovedEvent;
import org.briarproject.briar.R;

View File

@@ -2,11 +2,11 @@ package org.briarproject.briar.messaging;
import org.briarproject.bramble.BrambleCoreEagerSingletons;
import org.briarproject.bramble.BrambleCoreModule;
import org.briarproject.bramble.api.connection.ConnectionManager;
import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.plugin.ConnectionManager;
import org.briarproject.bramble.test.BrambleCoreIntegrationTestModule;
import org.briarproject.briar.api.messaging.MessagingManager;
import org.briarproject.briar.api.messaging.PrivateMessageFactory;

View File

@@ -3,13 +3,13 @@ package org.briarproject.briar.test;
import org.briarproject.bramble.BrambleCoreEagerSingletons;
import org.briarproject.bramble.BrambleCoreModule;
import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.connection.ConnectionManager;
import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.identity.AuthorFactory;
import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.plugin.ConnectionManager;
import org.briarproject.bramble.api.properties.TransportPropertyManager;
import org.briarproject.bramble.test.BrambleCoreIntegrationTestModule;
import org.briarproject.briar.api.blog.BlogFactory;

View File

@@ -11,7 +11,6 @@ import org.briarproject.bramble.api.lifecycle.IoExecutor
import org.briarproject.bramble.api.network.NetworkManager
import org.briarproject.bramble.api.plugin.BackoffFactory
import org.briarproject.bramble.api.plugin.PluginConfig
import org.briarproject.bramble.api.plugin.TransportId
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory
import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory
import org.briarproject.bramble.api.system.Clock
@@ -89,7 +88,6 @@ internal class HeadlessModule(private val appDir: File) {
override fun getDuplexFactories(): Collection<DuplexPluginFactory> = duplex
override fun getSimplexFactories(): Collection<SimplexPluginFactory> = emptyList()
override fun shouldPoll(): Boolean = true
override fun getTransportPreferences(): Map<TransportId, List<TransportId>> = emptyMap()
}
}

View File

@@ -6,7 +6,6 @@ import dagger.Provides
import org.briarproject.bramble.api.FeatureFlags
import org.briarproject.bramble.api.db.DatabaseConfig
import org.briarproject.bramble.api.plugin.PluginConfig
import org.briarproject.bramble.api.plugin.TransportId
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory
import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory
import org.briarproject.bramble.network.JavaNetworkModule
@@ -56,7 +55,6 @@ internal class HeadlessTestModule(private val appDir: File) {
override fun getDuplexFactories(): Collection<DuplexPluginFactory> = emptyList()
override fun getSimplexFactories(): Collection<SimplexPluginFactory> = emptyList()
override fun shouldPoll(): Boolean = false
override fun getTransportPreferences(): Map<TransportId, List<TransportId>> = emptyMap()
}
}