mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-15 20:29:52 +01:00
Refactoring.
Unidirectional transports and connections are now called simplex rather than batch. Bidirectional transports and connections are now called duplex rather than stream.
This commit is contained in:
@@ -0,0 +1,188 @@
|
||||
package net.sf.briar.protocol.simplex;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.Collections;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import net.sf.briar.BriarTestCase;
|
||||
import net.sf.briar.TestUtils;
|
||||
import net.sf.briar.api.ContactId;
|
||||
import net.sf.briar.api.db.DatabaseComponent;
|
||||
import net.sf.briar.api.db.DatabaseExecutor;
|
||||
import net.sf.briar.api.protocol.Ack;
|
||||
import net.sf.briar.api.protocol.BatchId;
|
||||
import net.sf.briar.api.protocol.ProtocolConstants;
|
||||
import net.sf.briar.api.protocol.ProtocolWriterFactory;
|
||||
import net.sf.briar.api.protocol.RawBatch;
|
||||
import net.sf.briar.api.protocol.TransportId;
|
||||
import net.sf.briar.api.protocol.TransportIndex;
|
||||
import net.sf.briar.api.protocol.UniqueId;
|
||||
import net.sf.briar.api.transport.ConnectionContext;
|
||||
import net.sf.briar.api.transport.ConnectionRegistry;
|
||||
import net.sf.briar.api.transport.ConnectionWriterFactory;
|
||||
import net.sf.briar.api.transport.TransportConstants;
|
||||
import net.sf.briar.crypto.CryptoModule;
|
||||
import net.sf.briar.protocol.ProtocolModule;
|
||||
import net.sf.briar.protocol.duplex.DuplexProtocolModule;
|
||||
import net.sf.briar.protocol.simplex.SimplexProtocolModule;
|
||||
import net.sf.briar.serial.SerialModule;
|
||||
import net.sf.briar.transport.TransportModule;
|
||||
|
||||
import org.jmock.Expectations;
|
||||
import org.jmock.Mockery;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.Module;
|
||||
|
||||
public class OutgoingSimplexConnectionTest extends BriarTestCase {
|
||||
|
||||
private final Mockery context;
|
||||
private final DatabaseComponent db;
|
||||
private final ConnectionRegistry connRegistry;
|
||||
private final ConnectionWriterFactory connFactory;
|
||||
private final ProtocolWriterFactory protoFactory;
|
||||
private final ContactId contactId;
|
||||
private final TransportId transportId;
|
||||
private final TransportIndex transportIndex;
|
||||
private final byte[] secret;
|
||||
|
||||
public OutgoingSimplexConnectionTest() {
|
||||
super();
|
||||
context = new Mockery();
|
||||
db = context.mock(DatabaseComponent.class);
|
||||
Module testModule = new AbstractModule() {
|
||||
@Override
|
||||
public void configure() {
|
||||
bind(DatabaseComponent.class).toInstance(db);
|
||||
bind(Executor.class).annotatedWith(
|
||||
DatabaseExecutor.class).toInstance(
|
||||
Executors.newCachedThreadPool());
|
||||
}
|
||||
};
|
||||
Injector i = Guice.createInjector(testModule, new CryptoModule(),
|
||||
new SerialModule(), new TransportModule(),
|
||||
new SimplexProtocolModule(), new ProtocolModule(),
|
||||
new DuplexProtocolModule());
|
||||
connRegistry = i.getInstance(ConnectionRegistry.class);
|
||||
connFactory = i.getInstance(ConnectionWriterFactory.class);
|
||||
protoFactory = i.getInstance(ProtocolWriterFactory.class);
|
||||
contactId = new ContactId(1);
|
||||
transportId = new TransportId(TestUtils.getRandomId());
|
||||
transportIndex = new TransportIndex(13);
|
||||
secret = new byte[32];
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConnectionTooShort() throws Exception {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
TestSimplexTransportWriter transport = new TestSimplexTransportWriter(
|
||||
out, ProtocolConstants.MAX_PACKET_LENGTH, true);
|
||||
OutgoingSimplexConnection connection = new OutgoingSimplexConnection(db,
|
||||
connRegistry, connFactory, protoFactory, contactId, transportId,
|
||||
transportIndex, transport);
|
||||
final ConnectionContext ctx = context.mock(ConnectionContext.class);
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(db).getConnectionContext(contactId, transportIndex);
|
||||
will(returnValue(ctx));
|
||||
oneOf(ctx).getSecret();
|
||||
will(returnValue(secret));
|
||||
}});
|
||||
connection.write();
|
||||
// Nothing should have been written
|
||||
assertEquals(0, out.size());
|
||||
// The transport should have been disposed with exception == true
|
||||
assertTrue(transport.getDisposed());
|
||||
assertTrue(transport.getException());
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNothingToSend() throws Exception {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
TestSimplexTransportWriter transport = new TestSimplexTransportWriter(
|
||||
out, TransportConstants.MIN_CONNECTION_LENGTH, true);
|
||||
OutgoingSimplexConnection connection = new OutgoingSimplexConnection(db,
|
||||
connRegistry, connFactory, protoFactory, contactId, transportId,
|
||||
transportIndex, transport);
|
||||
final ConnectionContext ctx = context.mock(ConnectionContext.class);
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(db).getConnectionContext(contactId, transportIndex);
|
||||
will(returnValue(ctx));
|
||||
oneOf(ctx).getSecret();
|
||||
will(returnValue(secret));
|
||||
// No transports to send
|
||||
oneOf(db).generateTransportUpdate(contactId);
|
||||
will(returnValue(null));
|
||||
// No subscriptions to send
|
||||
oneOf(db).generateSubscriptionUpdate(contactId);
|
||||
will(returnValue(null));
|
||||
// No acks to send
|
||||
oneOf(db).generateAck(with(contactId), with(any(int.class)));
|
||||
will(returnValue(null));
|
||||
// No batches to send
|
||||
oneOf(db).generateBatch(with(contactId), with(any(int.class)));
|
||||
will(returnValue(null));
|
||||
}});
|
||||
connection.write();
|
||||
// Nothing should have been written
|
||||
assertEquals(0, out.size());
|
||||
// The transport should have been disposed with exception == false
|
||||
assertTrue(transport.getDisposed());
|
||||
assertFalse(transport.getException());
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSomethingToSend() throws Exception {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
TestSimplexTransportWriter transport = new TestSimplexTransportWriter(
|
||||
out, TransportConstants.MIN_CONNECTION_LENGTH, true);
|
||||
OutgoingSimplexConnection connection = new OutgoingSimplexConnection(db,
|
||||
connRegistry, connFactory, protoFactory, contactId, transportId,
|
||||
transportIndex, transport);
|
||||
final ConnectionContext ctx = context.mock(ConnectionContext.class);
|
||||
final Ack ack = context.mock(Ack.class);
|
||||
final BatchId batchId = new BatchId(TestUtils.getRandomId());
|
||||
final RawBatch batch = context.mock(RawBatch.class);
|
||||
final byte[] message = new byte[1234];
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(db).getConnectionContext(contactId, transportIndex);
|
||||
will(returnValue(ctx));
|
||||
oneOf(ctx).getSecret();
|
||||
will(returnValue(secret));
|
||||
// No transports to send
|
||||
oneOf(db).generateTransportUpdate(contactId);
|
||||
will(returnValue(null));
|
||||
// No subscriptions to send
|
||||
oneOf(db).generateSubscriptionUpdate(contactId);
|
||||
will(returnValue(null));
|
||||
// One ack to send
|
||||
oneOf(db).generateAck(with(contactId), with(any(int.class)));
|
||||
will(returnValue(ack));
|
||||
oneOf(ack).getBatchIds();
|
||||
will(returnValue(Collections.singletonList(batchId)));
|
||||
// No more acks
|
||||
oneOf(db).generateAck(with(contactId), with(any(int.class)));
|
||||
will(returnValue(null));
|
||||
// One batch to send
|
||||
oneOf(db).generateBatch(with(contactId), with(any(int.class)));
|
||||
will(returnValue(batch));
|
||||
oneOf(batch).getMessages();
|
||||
will(returnValue(Collections.singletonList(message)));
|
||||
// No more batches
|
||||
oneOf(db).generateBatch(with(contactId), with(any(int.class)));
|
||||
will(returnValue(null));
|
||||
}});
|
||||
connection.write();
|
||||
// Something should have been written
|
||||
assertTrue(out.size() > UniqueId.LENGTH + message.length);
|
||||
// The transport should have been disposed with exception == false
|
||||
assertTrue(transport.getDisposed());
|
||||
assertFalse(transport.getException());
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,205 @@
|
||||
package net.sf.briar.protocol.simplex;
|
||||
|
||||
import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Random;
|
||||
|
||||
import net.sf.briar.BriarTestCase;
|
||||
import net.sf.briar.TestDatabaseModule;
|
||||
import net.sf.briar.TestUtils;
|
||||
import net.sf.briar.api.ContactId;
|
||||
import net.sf.briar.api.db.DatabaseComponent;
|
||||
import net.sf.briar.api.db.event.DatabaseEvent;
|
||||
import net.sf.briar.api.db.event.DatabaseListener;
|
||||
import net.sf.briar.api.db.event.MessagesAddedEvent;
|
||||
import net.sf.briar.api.protocol.Message;
|
||||
import net.sf.briar.api.protocol.MessageFactory;
|
||||
import net.sf.briar.api.protocol.ProtocolReaderFactory;
|
||||
import net.sf.briar.api.protocol.ProtocolWriterFactory;
|
||||
import net.sf.briar.api.protocol.Transport;
|
||||
import net.sf.briar.api.protocol.TransportId;
|
||||
import net.sf.briar.api.protocol.TransportIndex;
|
||||
import net.sf.briar.api.protocol.TransportUpdate;
|
||||
import net.sf.briar.api.transport.ConnectionContext;
|
||||
import net.sf.briar.api.transport.ConnectionReaderFactory;
|
||||
import net.sf.briar.api.transport.ConnectionRecogniser;
|
||||
import net.sf.briar.api.transport.ConnectionRegistry;
|
||||
import net.sf.briar.api.transport.ConnectionWriterFactory;
|
||||
import net.sf.briar.crypto.CryptoModule;
|
||||
import net.sf.briar.db.DatabaseModule;
|
||||
import net.sf.briar.lifecycle.LifecycleModule;
|
||||
import net.sf.briar.plugins.ImmediateExecutor;
|
||||
import net.sf.briar.protocol.ProtocolModule;
|
||||
import net.sf.briar.protocol.duplex.DuplexProtocolModule;
|
||||
import net.sf.briar.protocol.simplex.SimplexProtocolModule;
|
||||
import net.sf.briar.serial.SerialModule;
|
||||
import net.sf.briar.transport.TransportModule;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
|
||||
public class SimplexConnectionReadWriteTest extends BriarTestCase {
|
||||
|
||||
private final File testDir = TestUtils.getTestDirectory();
|
||||
private final File aliceDir = new File(testDir, "alice");
|
||||
private final File bobDir = new File(testDir, "bob");
|
||||
private final TransportId transportId;
|
||||
private final TransportIndex transportIndex;
|
||||
private final byte[] aliceToBobSecret, bobToAliceSecret;
|
||||
|
||||
private Injector alice, bob;
|
||||
|
||||
public SimplexConnectionReadWriteTest() throws Exception {
|
||||
super();
|
||||
transportId = new TransportId(TestUtils.getRandomId());
|
||||
transportIndex = new TransportIndex(1);
|
||||
// Create matching secrets for Alice and Bob
|
||||
Random r = new Random();
|
||||
aliceToBobSecret = new byte[32];
|
||||
r.nextBytes(aliceToBobSecret);
|
||||
bobToAliceSecret = new byte[32];
|
||||
r.nextBytes(bobToAliceSecret);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
testDir.mkdirs();
|
||||
alice = createInjector(aliceDir);
|
||||
bob = createInjector(bobDir);
|
||||
}
|
||||
|
||||
private Injector createInjector(File dir) {
|
||||
return Guice.createInjector(new CryptoModule(), new DatabaseModule(),
|
||||
new LifecycleModule(), new ProtocolModule(), new SerialModule(),
|
||||
new TestDatabaseModule(dir), new SimplexProtocolModule(),
|
||||
new TransportModule(), new DuplexProtocolModule());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInjection() {
|
||||
DatabaseComponent aliceDb = alice.getInstance(DatabaseComponent.class);
|
||||
DatabaseComponent bobDb = bob.getInstance(DatabaseComponent.class);
|
||||
assertFalse(aliceDb == bobDb);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteAndRead() throws Exception {
|
||||
read(write());
|
||||
}
|
||||
|
||||
private byte[] write() throws Exception {
|
||||
// Open Alice's database
|
||||
DatabaseComponent db = alice.getInstance(DatabaseComponent.class);
|
||||
db.open(false);
|
||||
// Add Bob as a contact and send him a message
|
||||
ContactId contactId = db.addContact(bobToAliceSecret, aliceToBobSecret);
|
||||
String subject = "Hello";
|
||||
byte[] body = "Hi Bob!".getBytes("UTF-8");
|
||||
MessageFactory messageFactory = alice.getInstance(MessageFactory.class);
|
||||
Message message = messageFactory.createMessage(null, subject, body);
|
||||
db.addLocalPrivateMessage(message, contactId);
|
||||
// Create an outgoing batch connection
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
ConnectionRegistry connRegistry =
|
||||
alice.getInstance(ConnectionRegistry.class);
|
||||
ConnectionWriterFactory connFactory =
|
||||
alice.getInstance(ConnectionWriterFactory.class);
|
||||
ProtocolWriterFactory protoFactory =
|
||||
alice.getInstance(ProtocolWriterFactory.class);
|
||||
TestSimplexTransportWriter transport = new TestSimplexTransportWriter(out,
|
||||
Long.MAX_VALUE, false);
|
||||
OutgoingSimplexConnection batchOut = new OutgoingSimplexConnection(db,
|
||||
connRegistry, connFactory, protoFactory, contactId, transportId,
|
||||
transportIndex, transport);
|
||||
// Write whatever needs to be written
|
||||
batchOut.write();
|
||||
assertTrue(transport.getDisposed());
|
||||
assertFalse(transport.getException());
|
||||
// Close Alice's database
|
||||
db.close();
|
||||
// Return the contents of the batch connection
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
private void read(byte[] b) throws Exception {
|
||||
// Open Bob's database
|
||||
DatabaseComponent db = bob.getInstance(DatabaseComponent.class);
|
||||
db.open(false);
|
||||
// Set up a database listener
|
||||
MessageListener listener = new MessageListener();
|
||||
db.addListener(listener);
|
||||
// Add Alice as a contact
|
||||
ContactId contactId = db.addContact(aliceToBobSecret, bobToAliceSecret);
|
||||
// Add the transport
|
||||
assertEquals(transportIndex, db.addTransport(transportId));
|
||||
// Fake a transport update from Alice
|
||||
TransportUpdate transportUpdate = new TransportUpdate() {
|
||||
|
||||
public Collection<Transport> getTransports() {
|
||||
Transport t = new Transport(transportId, transportIndex);
|
||||
return Collections.singletonList(t);
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
return System.currentTimeMillis();
|
||||
}
|
||||
};
|
||||
db.receiveTransportUpdate(contactId, transportUpdate);
|
||||
// Create a connection recogniser and recognise the connection
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(b);
|
||||
ConnectionRecogniser rec = bob.getInstance(ConnectionRecogniser.class);
|
||||
byte[] tag = new byte[TAG_LENGTH];
|
||||
int read = in.read(tag);
|
||||
assertEquals(tag.length, read);
|
||||
ConnectionContext ctx = rec.acceptConnection(transportId, tag);
|
||||
assertNotNull(ctx);
|
||||
assertEquals(contactId, ctx.getContactId());
|
||||
assertEquals(transportIndex, ctx.getTransportIndex());
|
||||
// Create an incoming batch connection
|
||||
ConnectionRegistry connRegistry =
|
||||
bob.getInstance(ConnectionRegistry.class);
|
||||
ConnectionReaderFactory connFactory =
|
||||
bob.getInstance(ConnectionReaderFactory.class);
|
||||
ProtocolReaderFactory protoFactory =
|
||||
bob.getInstance(ProtocolReaderFactory.class);
|
||||
TestSimplexTransportReader transport = new TestSimplexTransportReader(in);
|
||||
IncomingSimplexConnection batchIn = new IncomingSimplexConnection(
|
||||
new ImmediateExecutor(), new ImmediateExecutor(), db,
|
||||
connRegistry, connFactory, protoFactory, ctx, transportId,
|
||||
transport, tag);
|
||||
// No messages should have been added yet
|
||||
assertFalse(listener.messagesAdded);
|
||||
// Read whatever needs to be read
|
||||
batchIn.read();
|
||||
assertTrue(transport.getDisposed());
|
||||
assertFalse(transport.getException());
|
||||
assertTrue(transport.getRecognised());
|
||||
// The private message from Alice should have been added
|
||||
assertTrue(listener.messagesAdded);
|
||||
// Close Bob's database
|
||||
db.close();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
TestUtils.deleteTestDirectory(testDir);
|
||||
}
|
||||
|
||||
private static class MessageListener implements DatabaseListener {
|
||||
|
||||
private boolean messagesAdded = false;
|
||||
|
||||
public void eventOccurred(DatabaseEvent e) {
|
||||
if(e instanceof MessagesAddedEvent) messagesAdded = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package net.sf.briar.protocol.simplex;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
import net.sf.briar.api.plugins.SimplexTransportReader;
|
||||
|
||||
class TestSimplexTransportReader implements SimplexTransportReader {
|
||||
|
||||
private final InputStream in;
|
||||
|
||||
private boolean disposed = false, exception = false, recognised = false;
|
||||
|
||||
TestSimplexTransportReader(InputStream in) {
|
||||
this.in = in;
|
||||
}
|
||||
|
||||
public InputStream getInputStream() {
|
||||
return in;
|
||||
}
|
||||
|
||||
public void dispose(boolean exception, boolean recognised) {
|
||||
assert !disposed;
|
||||
disposed = true;
|
||||
this.exception = exception;
|
||||
this.recognised = recognised;
|
||||
}
|
||||
|
||||
boolean getDisposed() {
|
||||
return disposed;
|
||||
}
|
||||
|
||||
boolean getException() {
|
||||
return exception;
|
||||
}
|
||||
|
||||
boolean getRecognised() {
|
||||
return recognised;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package net.sf.briar.protocol.simplex;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import net.sf.briar.api.plugins.SimplexTransportWriter;
|
||||
|
||||
class TestSimplexTransportWriter implements SimplexTransportWriter {
|
||||
|
||||
private final ByteArrayOutputStream out;
|
||||
private final long capacity;
|
||||
private final boolean flush;
|
||||
|
||||
private boolean disposed = false, exception = false;
|
||||
|
||||
TestSimplexTransportWriter(ByteArrayOutputStream out, long capacity,
|
||||
boolean flush) {
|
||||
this.out = out;
|
||||
this.capacity = capacity;
|
||||
this.flush = flush;
|
||||
}
|
||||
|
||||
public long getCapacity() {
|
||||
return capacity;
|
||||
}
|
||||
|
||||
public OutputStream getOutputStream() {
|
||||
return out;
|
||||
}
|
||||
|
||||
public boolean shouldFlush() {
|
||||
return flush;
|
||||
}
|
||||
|
||||
public void dispose(boolean exception) {
|
||||
assert !disposed;
|
||||
disposed = true;
|
||||
this.exception = exception;
|
||||
}
|
||||
|
||||
boolean getDisposed() {
|
||||
return disposed;
|
||||
}
|
||||
|
||||
boolean getException() {
|
||||
return exception;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user