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:
akwizgran
2012-01-11 17:00:47 +00:00
parent 1499e061c1
commit 99caec9448
61 changed files with 505 additions and 515 deletions

View File

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

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}