mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 10:49:06 +01:00
Compare commits
32 Commits
1712-detec
...
1712-passi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f663bf8667 | ||
|
|
ef5b91da89 | ||
|
|
9909d205c7 | ||
|
|
9768b048d2 | ||
|
|
6dcad6c425 | ||
|
|
648f26542c | ||
|
|
730d553b0a | ||
|
|
7736a3b6fc | ||
|
|
95f427863d | ||
|
|
78d7fc2106 | ||
|
|
cc943be540 | ||
|
|
6eb77465f6 | ||
|
|
35d1b406f7 | ||
|
|
2add63657e | ||
|
|
d3751fbead | ||
|
|
4aaa8c3b93 | ||
|
|
5b04527c54 | ||
|
|
7d6b65913a | ||
|
|
36747acac1 | ||
|
|
e8dbc00712 | ||
|
|
d3d7212b08 | ||
|
|
2919657b4a | ||
|
|
0c338b362e | ||
|
|
8dd993dd9d | ||
|
|
1b2b50d91b | ||
|
|
ee9c771045 | ||
|
|
9e6d67f13d | ||
|
|
710b6d18ce | ||
|
|
dd4aa67643 | ||
|
|
79482d5e3a | ||
|
|
ee0bf7218c | ||
|
|
c1101c7fe1 |
@@ -67,7 +67,7 @@ public class AndroidBluetoothPluginFactory implements DuplexPluginFactory {
|
||||
@Override
|
||||
public DuplexPlugin createPlugin(PluginCallback callback) {
|
||||
BluetoothConnectionLimiter connectionLimiter =
|
||||
new BluetoothConnectionLimiterImpl();
|
||||
new BluetoothConnectionLimiterImpl(eventBus, clock);
|
||||
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
|
||||
MAX_POLLING_INTERVAL, BACKOFF_BASE);
|
||||
AndroidBluetoothPlugin plugin = new AndroidBluetoothPlugin(
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
package org.briarproject.bramble.api.plugin;
|
||||
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.TransportConnectionReader;
|
||||
import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
|
||||
import org.briarproject.bramble.api.plugin.TransportId;
|
||||
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
||||
|
||||
@NotNullByDefault
|
||||
@@ -0,0 +1,130 @@
|
||||
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);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
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();
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
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);
|
||||
}
|
||||
@@ -5,6 +5,8 @@ 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 {
|
||||
@@ -14,4 +16,11 @@ 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();
|
||||
}
|
||||
|
||||
@@ -13,13 +13,14 @@ public class ConnectionClosedEvent extends Event {
|
||||
|
||||
private final ContactId contactId;
|
||||
private final TransportId transportId;
|
||||
private final boolean incoming;
|
||||
private final boolean incoming, exception;
|
||||
|
||||
public ConnectionClosedEvent(ContactId contactId, TransportId transportId,
|
||||
boolean incoming) {
|
||||
boolean incoming, boolean exception) {
|
||||
this.contactId = contactId;
|
||||
this.transportId = transportId;
|
||||
this.incoming = incoming;
|
||||
this.exception = exception;
|
||||
}
|
||||
|
||||
public ContactId getContactId() {
|
||||
@@ -33,4 +34,8 @@ public class ConnectionClosedEvent extends Event {
|
||||
public boolean isIncoming() {
|
||||
return incoming;
|
||||
}
|
||||
|
||||
public boolean isException() {
|
||||
return exception;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
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);
|
||||
}
|
||||
@@ -10,4 +10,5 @@ public interface RecordTypes {
|
||||
byte OFFER = 2;
|
||||
byte REQUEST = 3;
|
||||
byte VERSIONS = 4;
|
||||
byte PRIORITY = 5;
|
||||
}
|
||||
|
||||
@@ -49,4 +49,10 @@ 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;
|
||||
}
|
||||
|
||||
@@ -28,4 +28,8 @@ public interface SyncRecordReader {
|
||||
boolean hasVersions() throws IOException;
|
||||
|
||||
Versions readVersions() throws IOException;
|
||||
|
||||
boolean hasPriority() throws IOException;
|
||||
|
||||
Priority readPriority() throws IOException;
|
||||
}
|
||||
|
||||
@@ -17,5 +17,7 @@ public interface SyncRecordWriter {
|
||||
|
||||
void writeVersions(Versions v) throws IOException;
|
||||
|
||||
void writePriority(Priority p) throws IOException;
|
||||
|
||||
void flush() throws IOException;
|
||||
}
|
||||
|
||||
@@ -2,18 +2,23 @@ package org.briarproject.bramble.api.sync;
|
||||
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.plugin.TransportId;
|
||||
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);
|
||||
SyncSession createIncomingSession(ContactId c, InputStream in,
|
||||
PriorityHandler handler);
|
||||
|
||||
SyncSession createSimplexOutgoingSession(ContactId c, int maxLatency,
|
||||
StreamWriter streamWriter);
|
||||
SyncSession createSimplexOutgoingSession(ContactId c, TransportId t,
|
||||
int maxLatency, StreamWriter streamWriter);
|
||||
|
||||
SyncSession createDuplexOutgoingSession(ContactId c, int maxLatency,
|
||||
int maxIdleTime, StreamWriter streamWriter);
|
||||
SyncSession createDuplexOutgoingSession(ContactId c, TransportId t,
|
||||
int maxLatency, int maxIdleTime, StreamWriter streamWriter,
|
||||
@Nullable Priority priority);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package org.briarproject.bramble.api.sync.event;
|
||||
|
||||
import org.briarproject.bramble.api.event.Event;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.plugin.TransportId;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
/**
|
||||
* An event that is broadcast when all sync connections using a given
|
||||
* transport should be closed.
|
||||
*/
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
public class CloseSyncConnectionsEvent extends Event {
|
||||
|
||||
private final TransportId transportId;
|
||||
|
||||
public CloseSyncConnectionsEvent(TransportId transportId) {
|
||||
this.transportId = transportId;
|
||||
}
|
||||
|
||||
public TransportId getTransportId() {
|
||||
return transportId;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
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;
|
||||
@@ -28,6 +29,7 @@ import dagger.Module;
|
||||
|
||||
@Module(includes = {
|
||||
ClientModule.class,
|
||||
ConnectionModule.class,
|
||||
ContactModule.class,
|
||||
CryptoModule.class,
|
||||
CryptoExecutorModule.class,
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
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));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,289 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -1,709 +0,0 @@
|
||||
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);
|
||||
ContactId c = requireNonNull(ctx.getContactId());
|
||||
return syncSessionFactory.createSimplexOutgoingSession(c,
|
||||
w.getMaxLatency(), streamWriter);
|
||||
}
|
||||
|
||||
private SyncSession createDuplexOutgoingSession(StreamContext ctx,
|
||||
TransportConnectionWriter w) throws IOException {
|
||||
StreamWriter streamWriter = streamWriterFactory.createStreamWriter(
|
||||
w.getOutputStream(), ctx);
|
||||
ContactId c = requireNonNull(ctx.getContactId());
|
||||
return syncSessionFactory.createDuplexOutgoingSession(c,
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,150 +0,0 @@
|
||||
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));
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -3,8 +3,6 @@ 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;
|
||||
|
||||
@@ -29,20 +27,6 @@ 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,
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
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;
|
||||
@@ -9,8 +11,6 @@ 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 (!c.isIncoming()) {
|
||||
// Connect to the disconnected contact
|
||||
// If an outgoing connection failed, try to reconnect
|
||||
if (!c.isIncoming() && c.isException()) {
|
||||
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.getConnectedContacts(t);
|
||||
connectionRegistry.getConnectedOrBetterContacts(t);
|
||||
Collection<Pair<TransportProperties, ConnectionHandler>>
|
||||
properties = new ArrayList<>();
|
||||
for (Entry<ContactId, TransportProperties> e : remote.entrySet()) {
|
||||
|
||||
@@ -3,9 +3,16 @@ 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.SECONDS;
|
||||
|
||||
@NotNullByDefault
|
||||
interface BluetoothConnectionLimiter {
|
||||
|
||||
/**
|
||||
* How long a connection must remain open before it's considered stable.
|
||||
*/
|
||||
long STABILITY_PERIOD_MS = SECONDS.toMillis(90);
|
||||
|
||||
/**
|
||||
* Informs the limiter that key agreement has started.
|
||||
*/
|
||||
@@ -23,17 +30,9 @@ interface BluetoothConnectionLimiter {
|
||||
boolean canOpenContactConnection();
|
||||
|
||||
/**
|
||||
* Informs the limiter that a contact connection has been opened. The
|
||||
* limiter may close the new connection if key agreement is in progress.
|
||||
* <p/>
|
||||
* Returns false if the limiter has closed the new connection.
|
||||
* Informs the limiter that the given connection has been opened.
|
||||
*/
|
||||
boolean contactConnectionOpened(DuplexTransportConnection conn);
|
||||
|
||||
/**
|
||||
* Informs the limiter that a key agreement connection has been opened.
|
||||
*/
|
||||
void keyAgreementConnectionOpened(DuplexTransportConnection conn);
|
||||
void connectionOpened(DuplexTransportConnection conn);
|
||||
|
||||
/**
|
||||
* Informs the limiter that the given connection has been closed.
|
||||
|
||||
@@ -1,46 +1,53 @@
|
||||
package org.briarproject.bramble.plugin.bluetooth;
|
||||
|
||||
import org.briarproject.bramble.api.event.EventBus;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
||||
import org.briarproject.bramble.api.sync.event.CloseSyncConnectionsEvent;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.annotation.concurrent.GuardedBy;
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
import static org.briarproject.bramble.api.plugin.BluetoothConstants.ID;
|
||||
|
||||
@NotNullByDefault
|
||||
@ThreadSafe
|
||||
class BluetoothConnectionLimiterImpl implements BluetoothConnectionLimiter {
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(BluetoothConnectionLimiterImpl.class.getName());
|
||||
getLogger(BluetoothConnectionLimiterImpl.class.getName());
|
||||
|
||||
private final EventBus eventBus;
|
||||
private final Clock clock;
|
||||
|
||||
private final Object lock = new Object();
|
||||
// The following are locking: lock
|
||||
private final LinkedList<DuplexTransportConnection> connections =
|
||||
new LinkedList<>();
|
||||
@GuardedBy("lock")
|
||||
private final List<ConnectionRecord> connections = new LinkedList<>();
|
||||
@GuardedBy("lock")
|
||||
private boolean keyAgreementInProgress = false;
|
||||
@GuardedBy("lock")
|
||||
private int connectionLimit = 2;
|
||||
|
||||
BluetoothConnectionLimiterImpl(EventBus eventBus, Clock clock) {
|
||||
this.eventBus = eventBus;
|
||||
this.clock = clock;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyAgreementStarted() {
|
||||
List<DuplexTransportConnection> close;
|
||||
synchronized (lock) {
|
||||
keyAgreementInProgress = true;
|
||||
close = new ArrayList<>(connections);
|
||||
connections.clear();
|
||||
}
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
LOG.info("Key agreement started, closing " + close.size() +
|
||||
" connections");
|
||||
}
|
||||
for (DuplexTransportConnection conn : close) tryToClose(conn);
|
||||
LOG.info("Key agreement started");
|
||||
eventBus.broadcast(new CloseSyncConnectionsEvent(ID));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -57,60 +64,81 @@ class BluetoothConnectionLimiterImpl implements BluetoothConnectionLimiter {
|
||||
if (keyAgreementInProgress) {
|
||||
LOG.info("Can't open contact connection during key agreement");
|
||||
return false;
|
||||
} else {
|
||||
}
|
||||
long now = clock.currentTimeMillis();
|
||||
countStableConnections(now);
|
||||
if (connections.size() < connectionLimit) {
|
||||
LOG.info("Can open contact connection");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contactConnectionOpened(DuplexTransportConnection conn) {
|
||||
boolean accept = true;
|
||||
synchronized (lock) {
|
||||
if (keyAgreementInProgress) {
|
||||
LOG.info("Refusing contact connection during key agreement");
|
||||
accept = false;
|
||||
} else {
|
||||
LOG.info("Accepting contact connection");
|
||||
connections.add(conn);
|
||||
LOG.info("Can't open contact connection due to limit");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!accept) tryToClose(conn);
|
||||
return accept;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyAgreementConnectionOpened(DuplexTransportConnection conn) {
|
||||
public void connectionOpened(DuplexTransportConnection conn) {
|
||||
synchronized (lock) {
|
||||
LOG.info("Accepting key agreement connection");
|
||||
connections.add(conn);
|
||||
}
|
||||
}
|
||||
|
||||
private void tryToClose(DuplexTransportConnection conn) {
|
||||
try {
|
||||
conn.getWriter().dispose(false);
|
||||
conn.getReader().dispose(false, false);
|
||||
} catch (IOException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
long now = clock.currentTimeMillis();
|
||||
countStableConnections(now);
|
||||
connections.add(new ConnectionRecord(conn, now));
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
LOG.info("Connection opened, " + connections.size() + " open");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connectionClosed(DuplexTransportConnection conn) {
|
||||
synchronized (lock) {
|
||||
connections.remove(conn);
|
||||
if (LOG.isLoggable(INFO))
|
||||
countStableConnections(clock.currentTimeMillis());
|
||||
Iterator<ConnectionRecord> it = connections.iterator();
|
||||
while (it.hasNext()) {
|
||||
if (it.next().conn == conn) {
|
||||
it.remove();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
LOG.info("Connection closed, " + connections.size() + " open");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void allConnectionsClosed() {
|
||||
synchronized (lock) {
|
||||
long now = clock.currentTimeMillis();
|
||||
countStableConnections(now);
|
||||
connections.clear();
|
||||
LOG.info("All connections closed");
|
||||
}
|
||||
}
|
||||
|
||||
@GuardedBy("lock")
|
||||
private void countStableConnections(long now) {
|
||||
int stable = 0;
|
||||
for (ConnectionRecord rec : connections) {
|
||||
if (now - rec.timeOpened >= STABILITY_PERIOD_MS) stable++;
|
||||
}
|
||||
if (stable > connectionLimit) {
|
||||
connectionLimit = stable;
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
LOG.info("Raising connection limit to " + connectionLimit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class ConnectionRecord {
|
||||
|
||||
private final DuplexTransportConnection conn;
|
||||
private final long timeOpened;
|
||||
|
||||
private ConnectionRecord(DuplexTransportConnection conn,
|
||||
long timeOpened) {
|
||||
this.conn = conn;
|
||||
this.timeOpened = timeOpened;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,10 +232,9 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
|
||||
return;
|
||||
}
|
||||
LOG.info("Connection received");
|
||||
if (connectionLimiter.contactConnectionOpened(conn)) {
|
||||
backoff.reset();
|
||||
callback.handleConnection(conn);
|
||||
}
|
||||
connectionLimiter.connectionOpened(conn);
|
||||
backoff.reset();
|
||||
callback.handleConnection(conn);
|
||||
if (!running) return;
|
||||
}
|
||||
}
|
||||
@@ -327,8 +326,8 @@ 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) return null;
|
||||
return connectionLimiter.contactConnectionOpened(conn) ? conn : null;
|
||||
if (conn != null) connectionLimiter.connectionOpened(conn);
|
||||
return conn;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -384,7 +383,7 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
|
||||
LOG.info("Connecting to key agreement UUID " + uuid);
|
||||
conn = connect(address, uuid);
|
||||
}
|
||||
if (conn != null) connectionLimiter.keyAgreementConnectionOpened(conn);
|
||||
if (conn != null) connectionLimiter.connectionOpened(conn);
|
||||
return conn;
|
||||
}
|
||||
|
||||
@@ -453,7 +452,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.keyAgreementConnectionOpened(conn);
|
||||
connectionLimiter.connectionOpened(conn);
|
||||
return new KeyAgreementConnection(conn, ID);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ 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;
|
||||
@@ -23,7 +24,6 @@ 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;
|
||||
|
||||
@@ -11,13 +11,16 @@ import org.briarproject.bramble.api.event.EventListener;
|
||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||
import org.briarproject.bramble.api.lifecycle.event.LifecycleEvent;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
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;
|
||||
import org.briarproject.bramble.api.sync.Versions;
|
||||
import org.briarproject.bramble.api.sync.event.CloseSyncConnectionsEvent;
|
||||
import org.briarproject.bramble.api.sync.event.GroupVisibilityUpdatedEvent;
|
||||
import org.briarproject.bramble.api.sync.event.MessageRequestedEvent;
|
||||
import org.briarproject.bramble.api.sync.event.MessageSharedEvent;
|
||||
@@ -35,6 +38,7 @@ 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;
|
||||
@@ -71,9 +75,12 @@ class DuplexOutgoingSession implements SyncSession, EventListener {
|
||||
private final EventBus eventBus;
|
||||
private final Clock clock;
|
||||
private final ContactId contactId;
|
||||
private final TransportId transportId;
|
||||
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);
|
||||
@@ -86,18 +93,21 @@ class DuplexOutgoingSession implements SyncSession, EventListener {
|
||||
private volatile boolean interrupted = false;
|
||||
|
||||
DuplexOutgoingSession(DatabaseComponent db, Executor dbExecutor,
|
||||
EventBus eventBus, Clock clock, ContactId contactId, int maxLatency,
|
||||
int maxIdleTime, StreamWriter streamWriter,
|
||||
SyncRecordWriter recordWriter) {
|
||||
EventBus eventBus, Clock clock, ContactId contactId,
|
||||
TransportId transportId, int maxLatency, int maxIdleTime,
|
||||
StreamWriter streamWriter, SyncRecordWriter recordWriter,
|
||||
@Nullable Priority priority) {
|
||||
this.db = db;
|
||||
this.dbExecutor = dbExecutor;
|
||||
this.eventBus = eventBus;
|
||||
this.clock = clock;
|
||||
this.contactId = contactId;
|
||||
this.transportId = transportId;
|
||||
this.maxLatency = maxLatency;
|
||||
this.maxIdleTime = maxIdleTime;
|
||||
this.streamWriter = streamWriter;
|
||||
this.recordWriter = recordWriter;
|
||||
this.priority = priority;
|
||||
writerTasks = new LinkedBlockingQueue<>();
|
||||
}
|
||||
|
||||
@@ -108,6 +118,8 @@ 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();
|
||||
@@ -223,6 +235,9 @@ class DuplexOutgoingSession implements SyncSession, EventListener {
|
||||
} else if (e instanceof LifecycleEvent) {
|
||||
LifecycleEvent l = (LifecycleEvent) e;
|
||||
if (l.getLifecycleState() == STOPPING) interrupt();
|
||||
} else if (e instanceof CloseSyncConnectionsEvent) {
|
||||
CloseSyncConnectionsEvent c = (CloseSyncConnectionsEvent) e;
|
||||
if (c.getTransportId().equals(transportId)) interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@ 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;
|
||||
@@ -47,17 +49,19 @@ 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) {
|
||||
SyncRecordReader recordReader, PriorityHandler priorityHandler) {
|
||||
this.db = db;
|
||||
this.dbExecutor = dbExecutor;
|
||||
this.eventBus = eventBus;
|
||||
this.contactId = contactId;
|
||||
this.recordReader = recordReader;
|
||||
this.priorityHandler = priorityHandler;
|
||||
}
|
||||
|
||||
@IoExecutor
|
||||
@@ -86,6 +90,9 @@ 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();
|
||||
|
||||
@@ -11,11 +11,13 @@ import org.briarproject.bramble.api.event.EventListener;
|
||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||
import org.briarproject.bramble.api.lifecycle.event.LifecycleEvent;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
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.SyncRecordWriter;
|
||||
import org.briarproject.bramble.api.sync.SyncSession;
|
||||
import org.briarproject.bramble.api.sync.Versions;
|
||||
import org.briarproject.bramble.api.sync.event.CloseSyncConnectionsEvent;
|
||||
import org.briarproject.bramble.api.transport.StreamWriter;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -56,6 +58,7 @@ class SimplexOutgoingSession implements SyncSession, EventListener {
|
||||
private final Executor dbExecutor;
|
||||
private final EventBus eventBus;
|
||||
private final ContactId contactId;
|
||||
private final TransportId transportId;
|
||||
private final int maxLatency;
|
||||
private final StreamWriter streamWriter;
|
||||
private final SyncRecordWriter recordWriter;
|
||||
@@ -65,12 +68,14 @@ class SimplexOutgoingSession implements SyncSession, EventListener {
|
||||
private volatile boolean interrupted = false;
|
||||
|
||||
SimplexOutgoingSession(DatabaseComponent db, Executor dbExecutor,
|
||||
EventBus eventBus, ContactId contactId, int maxLatency,
|
||||
StreamWriter streamWriter, SyncRecordWriter recordWriter) {
|
||||
EventBus eventBus, ContactId contactId, TransportId transportId,
|
||||
int maxLatency, StreamWriter streamWriter,
|
||||
SyncRecordWriter recordWriter) {
|
||||
this.db = db;
|
||||
this.dbExecutor = dbExecutor;
|
||||
this.eventBus = eventBus;
|
||||
this.contactId = contactId;
|
||||
this.transportId = transportId;
|
||||
this.maxLatency = maxLatency;
|
||||
this.streamWriter = streamWriter;
|
||||
this.recordWriter = recordWriter;
|
||||
@@ -123,6 +128,9 @@ class SimplexOutgoingSession implements SyncSession, EventListener {
|
||||
} else if (e instanceof LifecycleEvent) {
|
||||
LifecycleEvent l = (LifecycleEvent) e;
|
||||
if (l.getLifecycleState() == STOPPING) interrupt();
|
||||
} else if (e instanceof CloseSyncConnectionsEvent) {
|
||||
CloseSyncConnectionsEvent c = (CloseSyncConnectionsEvent) e;
|
||||
if (c.getTransportId().equals(transportId)) interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ 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;
|
||||
@@ -26,10 +27,12 @@ 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
|
||||
@@ -48,7 +51,7 @@ class SyncRecordReaderImpl implements SyncRecordReader {
|
||||
|
||||
private static boolean isKnownRecordType(byte type) {
|
||||
return type == ACK || type == MESSAGE || type == OFFER ||
|
||||
type == REQUEST || type == VERSIONS;
|
||||
type == REQUEST || type == VERSIONS || type == PRIORITY;
|
||||
}
|
||||
|
||||
private final MessageFactory messageFactory;
|
||||
@@ -174,4 +177,23 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ 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;
|
||||
@@ -20,6 +21,7 @@ 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;
|
||||
@@ -73,6 +75,12 @@ 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();
|
||||
|
||||
@@ -5,6 +5,9 @@ import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||
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;
|
||||
@@ -18,6 +21,7 @@ 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;
|
||||
|
||||
@@ -46,29 +50,32 @@ class SyncSessionFactoryImpl implements SyncSessionFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SyncSession createIncomingSession(ContactId c, InputStream in) {
|
||||
public SyncSession createIncomingSession(ContactId c, InputStream in,
|
||||
PriorityHandler handler) {
|
||||
SyncRecordReader recordReader =
|
||||
recordReaderFactory.createRecordReader(in);
|
||||
return new IncomingSession(db, dbExecutor, eventBus, c, recordReader);
|
||||
return new IncomingSession(db, dbExecutor, eventBus, c, recordReader,
|
||||
handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SyncSession createSimplexOutgoingSession(ContactId c,
|
||||
public SyncSession createSimplexOutgoingSession(ContactId c, TransportId t,
|
||||
int maxLatency, StreamWriter streamWriter) {
|
||||
OutputStream out = streamWriter.getOutputStream();
|
||||
SyncRecordWriter recordWriter =
|
||||
recordWriterFactory.createRecordWriter(out);
|
||||
return new SimplexOutgoingSession(db, dbExecutor, eventBus, c,
|
||||
return new SimplexOutgoingSession(db, dbExecutor, eventBus, c, t,
|
||||
maxLatency, streamWriter, recordWriter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SyncSession createDuplexOutgoingSession(ContactId c, int maxLatency,
|
||||
int maxIdleTime, StreamWriter streamWriter) {
|
||||
public SyncSession createDuplexOutgoingSession(ContactId c, TransportId t,
|
||||
int maxLatency, int maxIdleTime, StreamWriter streamWriter,
|
||||
@Nullable Priority priority) {
|
||||
OutputStream out = streamWriter.getOutputStream();
|
||||
SyncRecordWriter recordWriter =
|
||||
recordWriterFactory.createRecordWriter(out);
|
||||
return new DuplexOutgoingSession(db, dbExecutor, eventBus, clock, c,
|
||||
maxLatency, maxIdleTime, streamWriter, recordWriter);
|
||||
return new DuplexOutgoingSession(db, dbExecutor, eventBus, clock, c, t,
|
||||
maxLatency, maxIdleTime, streamWriter, recordWriter, priority);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,611 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -1,149 +0,0 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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,7 +157,21 @@ public class PollerImplTest extends BrambleMockTestCase {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRescheduleAndReconnectOnConnectionClosed()
|
||||
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()
|
||||
throws Exception {
|
||||
DuplexPlugin plugin = context.mock(DuplexPlugin.class);
|
||||
DuplexTransportConnection duplexConnection =
|
||||
@@ -166,45 +180,40 @@ 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));
|
||||
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));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -354,7 +363,7 @@ public class PollerImplTest extends BrambleMockTestCase {
|
||||
// Get the transport properties and connected contacts
|
||||
oneOf(transportPropertyManager).getRemoteProperties(transportId);
|
||||
will(returnValue(singletonMap(contactId, properties)));
|
||||
oneOf(connectionRegistry).getConnectedContacts(transportId);
|
||||
oneOf(connectionRegistry).getConnectedOrBetterContacts(transportId);
|
||||
will(returnValue(emptyList()));
|
||||
// Poll the plugin
|
||||
oneOf(plugin).poll(with(collectionOf(
|
||||
@@ -397,7 +406,7 @@ public class PollerImplTest extends BrambleMockTestCase {
|
||||
// Get the transport properties and connected contacts
|
||||
oneOf(transportPropertyManager).getRemoteProperties(transportId);
|
||||
will(returnValue(singletonMap(contactId, properties)));
|
||||
oneOf(connectionRegistry).getConnectedContacts(transportId);
|
||||
oneOf(connectionRegistry).getConnectedOrBetterContacts(transportId);
|
||||
will(returnValue(singletonList(contactId)));
|
||||
// All contacts are connected, so don't poll the plugin
|
||||
}});
|
||||
@@ -431,4 +440,48 @@ 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);
|
||||
}});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
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;
|
||||
@@ -13,7 +14,6 @@ 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;
|
||||
|
||||
@@ -4,6 +4,7 @@ import org.briarproject.bramble.api.contact.ContactId;
|
||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||
import org.briarproject.bramble.api.db.Transaction;
|
||||
import org.briarproject.bramble.api.event.EventBus;
|
||||
import org.briarproject.bramble.api.plugin.TransportId;
|
||||
import org.briarproject.bramble.api.sync.Ack;
|
||||
import org.briarproject.bramble.api.sync.GroupId;
|
||||
import org.briarproject.bramble.api.sync.Message;
|
||||
@@ -23,6 +24,7 @@ import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_IDS;
|
||||
import static org.briarproject.bramble.test.TestUtils.getContactId;
|
||||
import static org.briarproject.bramble.test.TestUtils.getMessage;
|
||||
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
||||
import static org.briarproject.bramble.test.TestUtils.getTransportId;
|
||||
|
||||
public class SimplexOutgoingSessionTest extends BrambleMockTestCase {
|
||||
|
||||
@@ -36,14 +38,15 @@ public class SimplexOutgoingSessionTest extends BrambleMockTestCase {
|
||||
|
||||
private final Executor dbExecutor = new ImmediateExecutor();
|
||||
private final ContactId contactId = getContactId();
|
||||
private final TransportId transportId = getTransportId();
|
||||
private final Message message = getMessage(new GroupId(getRandomId()));
|
||||
private final MessageId messageId = message.getId();
|
||||
|
||||
@Test
|
||||
public void testNothingToSend() throws Exception {
|
||||
SimplexOutgoingSession session = new SimplexOutgoingSession(db,
|
||||
dbExecutor, eventBus, contactId, MAX_LATENCY, streamWriter,
|
||||
recordWriter);
|
||||
dbExecutor, eventBus, contactId, transportId, MAX_LATENCY,
|
||||
streamWriter, recordWriter);
|
||||
Transaction noAckTxn = new Transaction(null, false);
|
||||
Transaction noMsgTxn = new Transaction(null, false);
|
||||
|
||||
@@ -76,8 +79,8 @@ public class SimplexOutgoingSessionTest extends BrambleMockTestCase {
|
||||
public void testSomethingToSend() throws Exception {
|
||||
Ack ack = new Ack(singletonList(messageId));
|
||||
SimplexOutgoingSession session = new SimplexOutgoingSession(db,
|
||||
dbExecutor, eventBus, contactId, MAX_LATENCY, streamWriter,
|
||||
recordWriter);
|
||||
dbExecutor, eventBus, contactId, transportId, MAX_LATENCY,
|
||||
streamWriter, recordWriter);
|
||||
Transaction ackTxn = new Transaction(null, false);
|
||||
Transaction noAckTxn = new Transaction(null, false);
|
||||
Transaction msgTxn = new Transaction(null, false);
|
||||
|
||||
@@ -8,6 +8,7 @@ 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;
|
||||
@@ -24,11 +25,14 @@ 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;
|
||||
@@ -119,6 +123,31 @@ 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());
|
||||
@@ -173,6 +202,11 @@ 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) {
|
||||
|
||||
@@ -10,12 +10,15 @@ 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;
|
||||
|
||||
@@ -85,6 +88,12 @@ public class TestPluginConfigModule {
|
||||
public boolean shouldPoll() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<TransportId, List<TransportId>> getTransportPreferences() {
|
||||
return emptyMap();
|
||||
}
|
||||
|
||||
};
|
||||
return pluginConfig;
|
||||
}
|
||||
|
||||
@@ -6,10 +6,14 @@ 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;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import org.briarproject.bramble.plugin.bluetooth.JavaBluetoothPluginFactory;
|
||||
import org.briarproject.bramble.plugin.modem.ModemPluginFactory;
|
||||
import org.briarproject.bramble.plugin.tcp.LanTcpPluginFactory;
|
||||
@@ -17,6 +21,8 @@ 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;
|
||||
@@ -24,6 +30,8 @@ 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 {
|
||||
@@ -32,10 +40,11 @@ public class DesktopPluginModule extends PluginModule {
|
||||
PluginConfig getPluginConfig(@IoExecutor Executor ioExecutor,
|
||||
SecureRandom random, BackoffFactory backoffFactory,
|
||||
ReliabilityLayerFactory reliabilityFactory,
|
||||
ShutdownManager shutdownManager, EventBus eventBus,
|
||||
ShutdownManager shutdownManager, EventBus eventBus, Clock clock,
|
||||
TimeoutMonitor timeoutMonitor) {
|
||||
DuplexPluginFactory bluetooth = new JavaBluetoothPluginFactory(
|
||||
ioExecutor, random, eventBus, timeoutMonitor, backoffFactory);
|
||||
ioExecutor, random, eventBus, clock, timeoutMonitor,
|
||||
backoffFactory);
|
||||
DuplexPluginFactory modem = new ModemPluginFactory(ioExecutor,
|
||||
reliabilityFactory);
|
||||
DuplexPluginFactory lan = new LanTcpPluginFactory(ioExecutor,
|
||||
@@ -61,6 +70,13 @@ 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;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import org.briarproject.bramble.api.plugin.PluginCallback;
|
||||
import org.briarproject.bramble.api.plugin.TransportId;
|
||||
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
|
||||
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
import java.util.concurrent.Executor;
|
||||
@@ -30,15 +31,17 @@ public class JavaBluetoothPluginFactory implements DuplexPluginFactory {
|
||||
private final Executor ioExecutor;
|
||||
private final SecureRandom secureRandom;
|
||||
private final EventBus eventBus;
|
||||
private final Clock clock;
|
||||
private final TimeoutMonitor timeoutMonitor;
|
||||
private final BackoffFactory backoffFactory;
|
||||
|
||||
public JavaBluetoothPluginFactory(Executor ioExecutor,
|
||||
SecureRandom secureRandom, EventBus eventBus,
|
||||
SecureRandom secureRandom, EventBus eventBus, Clock clock,
|
||||
TimeoutMonitor timeoutMonitor, BackoffFactory backoffFactory) {
|
||||
this.ioExecutor = ioExecutor;
|
||||
this.secureRandom = secureRandom;
|
||||
this.eventBus = eventBus;
|
||||
this.clock = clock;
|
||||
this.timeoutMonitor = timeoutMonitor;
|
||||
this.backoffFactory = backoffFactory;
|
||||
}
|
||||
@@ -56,7 +59,7 @@ public class JavaBluetoothPluginFactory implements DuplexPluginFactory {
|
||||
@Override
|
||||
public DuplexPlugin createPlugin(PluginCallback callback) {
|
||||
BluetoothConnectionLimiter connectionLimiter =
|
||||
new BluetoothConnectionLimiterImpl();
|
||||
new BluetoothConnectionLimiterImpl(eventBus, clock);
|
||||
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
|
||||
MAX_POLLING_INTERVAL, BACKOFF_BASE);
|
||||
JavaBluetoothPlugin plugin = new JavaBluetoothPlugin(connectionLimiter,
|
||||
|
||||
@@ -3,7 +3,7 @@ package org.briarproject.briar.android;
|
||||
import android.app.Activity;
|
||||
import android.util.Log;
|
||||
|
||||
import org.briarproject.bramble.api.plugin.ConnectionRegistry;
|
||||
import org.briarproject.bramble.api.connection.ConnectionRegistry;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import org.briarproject.briar.api.test.TestDataCreator;
|
||||
import org.junit.ClassRule;
|
||||
|
||||
@@ -120,7 +120,8 @@ public class SetupDataTest extends ScreenshotTest {
|
||||
|
||||
// TODO add messages
|
||||
|
||||
connectionRegistry.registerConnection(bob.getId(), ID, true);
|
||||
connectionRegistry.registerIncomingConnection(bob.getId(), ID, () -> {
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ 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;
|
||||
@@ -19,7 +20,6 @@ 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;
|
||||
|
||||
@@ -20,7 +20,10 @@ 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;
|
||||
@@ -48,6 +51,8 @@ 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;
|
||||
|
||||
@@ -62,6 +67,8 @@ 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;
|
||||
@@ -153,6 +160,13 @@ 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;
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ 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;
|
||||
@@ -24,7 +25,6 @@ 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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ 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;
|
||||
@@ -33,7 +34,6 @@ 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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -2,6 +2,7 @@ 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;
|
||||
@@ -10,7 +11,6 @@ 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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -5,6 +5,7 @@ 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;
|
||||
@@ -13,7 +14,6 @@ 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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -11,6 +11,7 @@ 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
|
||||
@@ -88,6 +89,7 @@ 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()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ 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
|
||||
@@ -55,6 +56,7 @@ 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()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user