Removed batches from BMP. Messages are now sent and acked individually.

This commit is contained in:
akwizgran
2013-01-16 22:56:03 +00:00
parent 13cad40004
commit 50ad1f486e
55 changed files with 574 additions and 1666 deletions

View File

@@ -11,13 +11,11 @@ import net.sf.briar.api.TransportProperties;
import net.sf.briar.api.db.event.DatabaseListener; import net.sf.briar.api.db.event.DatabaseListener;
import net.sf.briar.api.protocol.Ack; import net.sf.briar.api.protocol.Ack;
import net.sf.briar.api.protocol.AuthorId; import net.sf.briar.api.protocol.AuthorId;
import net.sf.briar.api.protocol.Batch;
import net.sf.briar.api.protocol.Group; import net.sf.briar.api.protocol.Group;
import net.sf.briar.api.protocol.GroupId; import net.sf.briar.api.protocol.GroupId;
import net.sf.briar.api.protocol.Message; import net.sf.briar.api.protocol.Message;
import net.sf.briar.api.protocol.MessageId; import net.sf.briar.api.protocol.MessageId;
import net.sf.briar.api.protocol.Offer; import net.sf.briar.api.protocol.Offer;
import net.sf.briar.api.protocol.RawBatch;
import net.sf.briar.api.protocol.Request; import net.sf.briar.api.protocol.Request;
import net.sf.briar.api.protocol.SubscriptionUpdate; import net.sf.briar.api.protocol.SubscriptionUpdate;
import net.sf.briar.api.protocol.Transport; import net.sf.briar.api.protocol.Transport;
@@ -70,25 +68,28 @@ public interface DatabaseComponent {
/** /**
* Generates an acknowledgement for the given contact. Returns null if * Generates an acknowledgement for the given contact. Returns null if
* there are no batches to acknowledge. * there are no messages to acknowledge.
*/ */
Ack generateAck(ContactId c, int maxBatches) throws DbException; Ack generateAck(ContactId c, int maxMessages) throws DbException;
/** /**
* Generates a batch of messages for the given contact. Returns null if * Generates a batch of raw messages for the given contact, with a total
* there are no sendable messages that fit in the given capacity. * length less than or equal to the given length. Returns null if
* there are no sendable messages that fit in the given length.
*/ */
RawBatch generateBatch(ContactId c, int capacity) throws DbException; Collection<byte[]> generateBatch(ContactId c, int maxLength)
throws DbException;
/** /**
* Generates a batch of messages for the given contact from the given * Generates a batch of raw messages for the given contact from the given
* collection of requested messages. Any messages that were either added to * collection of requested messages, with a total length less than or equal
* the batch, or were considered but are no longer sendable to the contact, * to the given length. Any messages that were either added to the batch,
* are removed from the collection of requested messages before returning. * or were considered but are no longer sendable to the contact, are
* removed from the collection of requested messages before returning.
* Returns null if there are no sendable messages that fit in the given * Returns null if there are no sendable messages that fit in the given
* capacity. * length.
*/ */
RawBatch generateBatch(ContactId c, int capacity, Collection<byte[]> generateBatch(ContactId c, int maxLength,
Collection<MessageId> requested) throws DbException; Collection<MessageId> requested) throws DbException;
/** /**
@@ -170,8 +171,8 @@ public interface DatabaseComponent {
/** Processes an acknowledgement from the given contact. */ /** Processes an acknowledgement from the given contact. */
void receiveAck(ContactId c, Ack a) throws DbException; void receiveAck(ContactId c, Ack a) throws DbException;
/** Processes a batch of messages from the given contact. */ /** Processes a message from the given contact. */
void receiveBatch(ContactId c, Batch b) throws DbException; void receiveMessage(ContactId c, Message m) throws DbException;
/** /**
* Processes an offer from the given contact and generates a request for * Processes an offer from the given contact and generates a request for

View File

@@ -1,6 +0,0 @@
package net.sf.briar.api.db.event;
/** An event that is broadcast when a batch of messages is received. */
public class BatchReceivedEvent extends DatabaseEvent {
}

View File

@@ -4,6 +4,6 @@ package net.sf.briar.api.db.event;
* An event that is broadcast when one or more messages are added to the * An event that is broadcast when one or more messages are added to the
* database. * database.
*/ */
public class MessagesAddedEvent extends DatabaseEvent { public class MessageAddedEvent extends DatabaseEvent {
} }

View File

@@ -0,0 +1,6 @@
package net.sf.briar.api.db.event;
/** An event that is broadcast when a message is received. */
public class MessageReceivedEvent extends DatabaseEvent {
}

View File

@@ -2,9 +2,9 @@ package net.sf.briar.api.protocol;
import java.util.Collection; import java.util.Collection;
/** A packet acknowledging receipt of one or more batches. */ /** A packet acknowledging receipt of one or more messages. */
public interface Ack { public interface Ack {
/** Returns the IDs of the acknowledged batches. */ /** Returns the IDs of the acknowledged messages. */
Collection<BatchId> getBatchIds(); Collection<MessageId> getMessageIds();
} }

View File

@@ -1,13 +0,0 @@
package net.sf.briar.api.protocol;
import java.util.Collection;
/** An incoming packet containing messages. */
public interface Batch {
/** Returns the batch's unique identifier. */
BatchId getId();
/** Returns the messages contained in the batch. */
Collection<Message> getMessages();
}

View File

@@ -1,21 +0,0 @@
package net.sf.briar.api.protocol;
import java.util.Arrays;
/**
* Type-safe wrapper for a byte array that uniquely identifies a batch of
* messages.
*/
public class BatchId extends UniqueId {
public BatchId(byte[] id) {
super(id);
}
@Override
public boolean equals(Object o) {
if(o instanceof BatchId)
return Arrays.equals(id, ((BatchId) o).id);
return false;
}
}

View File

@@ -0,0 +1,8 @@
package net.sf.briar.api.protocol;
import java.security.GeneralSecurityException;
public interface MessageVerifier {
Message verifyMessage(UnverifiedMessage m) throws GeneralSecurityException;
}

View File

@@ -6,9 +6,7 @@ import java.util.Map;
public interface PacketFactory { public interface PacketFactory {
Ack createAck(Collection<BatchId> acked); Ack createAck(Collection<MessageId> acked);
RawBatch createBatch(Collection<byte[]> messages);
Offer createOffer(Collection<MessageId> offered); Offer createOffer(Collection<MessageId> offered);

View File

@@ -9,8 +9,8 @@ public interface ProtocolReader {
boolean hasAck() throws IOException; boolean hasAck() throws IOException;
Ack readAck() throws IOException; Ack readAck() throws IOException;
boolean hasBatch() throws IOException; boolean hasMessage() throws IOException;
UnverifiedBatch readBatch() throws IOException; UnverifiedMessage readMessage() throws IOException;
boolean hasOffer() throws IOException; boolean hasOffer() throws IOException;
Offer readOffer() throws IOException; Offer readOffer() throws IOException;

View File

@@ -4,15 +4,13 @@ import java.io.IOException;
public interface ProtocolWriter { public interface ProtocolWriter {
int getMaxBatchesForAck(long capacity); int getMaxMessagesForAck(long capacity);
int getMaxMessagesForOffer(long capacity); int getMaxMessagesForOffer(long capacity);
int getMessageCapacityForBatch(long capacity);
void writeAck(Ack a) throws IOException; void writeAck(Ack a) throws IOException;
void writeBatch(RawBatch b) throws IOException; void writeMessage(byte[] raw) throws IOException;
void writeOffer(Offer o) throws IOException; void writeOffer(Offer o) throws IOException;

View File

@@ -1,13 +0,0 @@
package net.sf.briar.api.protocol;
import java.util.Collection;
/** An outgoing packet containing messages. */
public interface RawBatch {
/** Returns the batch's unique identifier. */
BatchId getId();
/** Returns the serialised messages contained in the batch. */
Collection<byte[]> getMessages();
}

View File

@@ -5,7 +5,6 @@ public interface Types {
int ACK = 0; int ACK = 0;
int AUTHOR = 1; int AUTHOR = 1;
int BATCH = 2;
int GROUP = 3; int GROUP = 3;
int MESSAGE = 4; int MESSAGE = 4;
int OFFER = 5; int OFFER = 5;

View File

@@ -1,8 +0,0 @@
package net.sf.briar.api.protocol;
import java.security.GeneralSecurityException;
public interface UnverifiedBatch {
Batch verify() throws GeneralSecurityException;
}

View File

@@ -1,10 +1,6 @@
package net.sf.briar.protocol; package net.sf.briar.api.protocol;
import net.sf.briar.api.protocol.Author; public interface UnverifiedMessage {
import net.sf.briar.api.protocol.Group;
import net.sf.briar.api.protocol.MessageId;
interface UnverifiedMessage {
MessageId getParent(); MessageId getParent();
@@ -16,7 +12,7 @@ interface UnverifiedMessage {
long getTimestamp(); long getTimestamp();
byte[] getRaw(); byte[] getSerialised();
byte[] getAuthorSignature(); byte[] getAuthorSignature();

View File

@@ -13,6 +13,7 @@
<classpathentry kind="lib" path="libs/weupnp-0.1.1.jar"/> <classpathentry kind="lib" path="libs/weupnp-0.1.1.jar"/>
<classpathentry kind="lib" path="libs/bluecove-2.1.1-SNAPSHOT-briar.jar"/> <classpathentry kind="lib" path="libs/bluecove-2.1.1-SNAPSHOT-briar.jar"/>
<classpathentry kind="lib" path="libs/bluecove-gpl-2.1.1-SNAPSHOT.jar"/> <classpathentry kind="lib" path="libs/bluecove-gpl-2.1.1-SNAPSHOT.jar"/>
<classpathentry kind="lib" path="libs/javax.inject.jar"/>
<classpathentry combineaccessrules="false" kind="src" path="/briar-api"/> <classpathentry combineaccessrules="false" kind="src" path="/briar-api"/>
<classpathentry kind="lib" path="/briar-api/libs/android.jar"/> <classpathentry kind="lib" path="/briar-api/libs/android.jar"/>
<classpathentry kind="lib" path="/briar-api/libs/guice-3.0-no_aop.jar"/> <classpathentry kind="lib" path="/briar-api/libs/guice-3.0-no_aop.jar"/>

View File

@@ -11,7 +11,6 @@ import net.sf.briar.api.TransportProperties;
import net.sf.briar.api.db.DbException; import net.sf.briar.api.db.DbException;
import net.sf.briar.api.db.MessageHeader; import net.sf.briar.api.db.MessageHeader;
import net.sf.briar.api.protocol.AuthorId; import net.sf.briar.api.protocol.AuthorId;
import net.sf.briar.api.protocol.BatchId;
import net.sf.briar.api.protocol.Group; import net.sf.briar.api.protocol.Group;
import net.sf.briar.api.protocol.GroupId; import net.sf.briar.api.protocol.GroupId;
import net.sf.briar.api.protocol.Message; import net.sf.briar.api.protocol.Message;
@@ -71,13 +70,6 @@ interface Database<T> {
*/ */
void commitTransaction(T txn) throws DbException; void commitTransaction(T txn) throws DbException;
/**
* Records a received batch as needing to be acknowledged.
* <p>
* Locking: contact read, messageStatus write.
*/
void addBatchToAck(T txn, ContactId c, BatchId b) throws DbException;
/** /**
* Adds a new contact to the database and returns an ID for the contact. * Adds a new contact to the database and returns an ID for the contact.
* <p> * <p>
@@ -101,12 +93,19 @@ interface Database<T> {
boolean addGroupMessage(T txn, Message m) throws DbException; boolean addGroupMessage(T txn, Message m) throws DbException;
/** /**
* Records a sent batch as needing to be acknowledged. * Records a received message as needing to be acknowledged.
* <p>
* Locking: contact read, messageStatus write.
*/
void addMessageToAck(T txn, ContactId c, MessageId m) throws DbException;
/**
* Records a collection of sent messages as needing to be acknowledged.
* <p> * <p>
* Locking: contact read, message read, messageStatus write. * Locking: contact read, message read, messageStatus write.
*/ */
void addOutstandingBatch(T txn, ContactId c, BatchId b, void addOutstandingMessages(T txn, ContactId c, Collection<MessageId> sent)
Collection<MessageId> sent) throws DbException; throws DbException;
/** /**
* Returns false if the given message is already in the database. Otherwise * Returns false if the given message is already in the database. Otherwise
@@ -196,15 +195,6 @@ interface Database<T> {
boolean containsVisibleSubscription(T txn, GroupId g, ContactId c, boolean containsVisibleSubscription(T txn, GroupId g, ContactId c,
long time) throws DbException; long time) throws DbException;
/**
* Returns the IDs of any batches received from the given contact that need
* to be acknowledged.
* <p>
* Locking: contact read, messageStatus read.
*/
Collection<BatchId> getBatchesToAck(T txn, ContactId c, int maxBatches)
throws DbException;
/** /**
* Returns the configuration for the given transport. * Returns the configuration for the given transport.
* <p> * <p>
@@ -267,12 +257,13 @@ interface Database<T> {
Collection<Transport> getLocalTransports(T txn) throws DbException; Collection<Transport> getLocalTransports(T txn) throws DbException;
/** /**
* Returns the IDs of any batches sent to the given contact that should now * Returns the IDs of any messages sent to the given contact that should
* be considered lost. * now be considered lost.
* <p> * <p>
* Locking: contact read, message read, messageStatus read. * Locking: contact read, message read, messageStatus read.
*/ */
Collection<BatchId> getLostBatches(T txn, ContactId c) throws DbException; Collection<MessageId> getLostMessages(T txn, ContactId c)
throws DbException;
/** /**
* Returns the message identified by the given ID, in serialised form. * Returns the message identified by the given ID, in serialised form.
@@ -315,6 +306,15 @@ interface Database<T> {
Collection<MessageId> getMessagesByAuthor(T txn, AuthorId a) Collection<MessageId> getMessagesByAuthor(T txn, AuthorId a)
throws DbException; throws DbException;
/**
* Returns the IDs of any messages received from the given contact that
* need to be acknowledged.
* <p>
* Locking: contact read, messageStatus read.
*/
Collection<MessageId> getMessagesToAck(T txn, ContactId c, int maxMessages)
throws DbException;
/** /**
* Returns the number of children of the message identified by the given * Returns the number of children of the message identified by the given
* ID that are present in the database and have sendability scores greater * ID that are present in the database and have sendability scores greater
@@ -380,12 +380,13 @@ interface Database<T> {
/** /**
* Returns the IDs of some messages that are eligible to be sent to the * Returns the IDs of some messages that are eligible to be sent to the
* given contact, with a total size less than or equal to the given size. * given contact, with a total length less than or equal to the given
* length.
* <p> * <p>
* Locking: contact read, message read, messageStatus read, * Locking: contact read, message read, messageStatus read,
* subscription read. * subscription read.
*/ */
Collection<MessageId> getSendableMessages(T txn, ContactId c, int capacity) Collection<MessageId> getSendableMessages(T txn, ContactId c, int maxLength)
throws DbException; throws DbException;
/** /**
@@ -493,21 +494,22 @@ interface Database<T> {
throws DbException; throws DbException;
/** /**
* Removes an outstanding batch that has been acknowledged. Any messages in * Removes outstanding messages that have been acknowledged. Any of the
* the batch that are still considered outstanding (Status.SENT) with * messages that are still considered outstanding (Status.SENT) with
* respect to the given contact are now considered seen (Status.SEEN). * respect to the given contact are now considered seen (Status.SEEN).
* <p> * <p>
* Locking: contact read, message read, messageStatus write. * Locking: contact read, message read, messageStatus write.
*/ */
void removeAckedBatch(T txn, ContactId c, BatchId b) throws DbException; void removeAckedMessages(T txn, ContactId c, Collection<MessageId> acked)
throws DbException;
/** /**
* Marks the given batches received from the given contact as having been * Marks the given messages received from the given contact as having been
* acknowledged. * acknowledged.
* <p> * <p>
* Locking: contact read, messageStatus write. * Locking: contact read, messageStatus write.
*/ */
void removeBatchesToAck(T txn, ContactId c, Collection<BatchId> sent) void removeMessagesToAck(T txn, ContactId c, Collection<MessageId> acked)
throws DbException; throws DbException;
/** /**
@@ -519,13 +521,14 @@ interface Database<T> {
void removeContact(T txn, ContactId c) throws DbException; void removeContact(T txn, ContactId c) throws DbException;
/** /**
* Removes an outstanding batch that has been lost. Any messages in the * Removes outstanding messages that have been lost. Any messages that are
* batch that are still considered outstanding (Status.SENT) with respect * still considered outstanding (Status.SENT) with respect to the given
* to the given contact are now considered unsent (Status.NEW). * contact are now considered unsent (Status.NEW).
* <p> * <p>
* Locking: contact read, message read, messageStatus write. * Locking: contact read, message read, messageStatus write.
*/ */
void removeLostBatch(T txn, ContactId c, BatchId b) throws DbException; void removeLostMessages(T txn, ContactId c, Collection<MessageId> lost)
throws DbException;
/** /**
* Removes a message (and all associated state) from the database. * Removes a message (and all associated state) from the database.

View File

@@ -32,28 +32,25 @@ import net.sf.briar.api.db.DbException;
import net.sf.briar.api.db.MessageHeader; import net.sf.briar.api.db.MessageHeader;
import net.sf.briar.api.db.NoSuchContactException; import net.sf.briar.api.db.NoSuchContactException;
import net.sf.briar.api.db.NoSuchContactTransportException; import net.sf.briar.api.db.NoSuchContactTransportException;
import net.sf.briar.api.db.event.BatchReceivedEvent;
import net.sf.briar.api.db.event.ContactAddedEvent; import net.sf.briar.api.db.event.ContactAddedEvent;
import net.sf.briar.api.db.event.ContactRemovedEvent; import net.sf.briar.api.db.event.ContactRemovedEvent;
import net.sf.briar.api.db.event.DatabaseEvent; import net.sf.briar.api.db.event.DatabaseEvent;
import net.sf.briar.api.db.event.DatabaseListener; import net.sf.briar.api.db.event.DatabaseListener;
import net.sf.briar.api.db.event.LocalTransportsUpdatedEvent; import net.sf.briar.api.db.event.LocalTransportsUpdatedEvent;
import net.sf.briar.api.db.event.MessagesAddedEvent; import net.sf.briar.api.db.event.MessageAddedEvent;
import net.sf.briar.api.db.event.MessageReceivedEvent;
import net.sf.briar.api.db.event.RatingChangedEvent; import net.sf.briar.api.db.event.RatingChangedEvent;
import net.sf.briar.api.db.event.RemoteTransportsUpdatedEvent; import net.sf.briar.api.db.event.RemoteTransportsUpdatedEvent;
import net.sf.briar.api.db.event.SubscriptionsUpdatedEvent; import net.sf.briar.api.db.event.SubscriptionsUpdatedEvent;
import net.sf.briar.api.lifecycle.ShutdownManager; import net.sf.briar.api.lifecycle.ShutdownManager;
import net.sf.briar.api.protocol.Ack; import net.sf.briar.api.protocol.Ack;
import net.sf.briar.api.protocol.AuthorId; import net.sf.briar.api.protocol.AuthorId;
import net.sf.briar.api.protocol.Batch;
import net.sf.briar.api.protocol.BatchId;
import net.sf.briar.api.protocol.Group; import net.sf.briar.api.protocol.Group;
import net.sf.briar.api.protocol.GroupId; import net.sf.briar.api.protocol.GroupId;
import net.sf.briar.api.protocol.Message; import net.sf.briar.api.protocol.Message;
import net.sf.briar.api.protocol.MessageId; import net.sf.briar.api.protocol.MessageId;
import net.sf.briar.api.protocol.Offer; import net.sf.briar.api.protocol.Offer;
import net.sf.briar.api.protocol.PacketFactory; import net.sf.briar.api.protocol.PacketFactory;
import net.sf.briar.api.protocol.RawBatch;
import net.sf.briar.api.protocol.Request; import net.sf.briar.api.protocol.Request;
import net.sf.briar.api.protocol.SubscriptionUpdate; import net.sf.briar.api.protocol.SubscriptionUpdate;
import net.sf.briar.api.protocol.Transport; import net.sf.briar.api.protocol.Transport;
@@ -270,7 +267,7 @@ DatabaseCleaner.Callback {
contactLock.readLock().unlock(); contactLock.readLock().unlock();
} }
// Call the listeners outside the lock // Call the listeners outside the lock
if(added) callListeners(new MessagesAddedEvent()); if(added) callListeners(new MessageAddedEvent());
} }
/** /**
@@ -388,7 +385,7 @@ DatabaseCleaner.Callback {
contactLock.readLock().unlock(); contactLock.readLock().unlock();
} }
// Call the listeners outside the lock // Call the listeners outside the lock
if(added) callListeners(new MessagesAddedEvent()); if(added) callListeners(new MessageAddedEvent());
} }
public void addSecrets(Collection<TemporarySecret> secrets) public void addSecrets(Collection<TemporarySecret> secrets)
@@ -444,8 +441,8 @@ DatabaseCleaner.Callback {
return true; return true;
} }
public Ack generateAck(ContactId c, int maxBatches) throws DbException { public Ack generateAck(ContactId c, int maxMessages) throws DbException {
Collection<BatchId> acked; Collection<MessageId> acked;
contactLock.readLock().lock(); contactLock.readLock().lock();
try { try {
messageStatusLock.readLock().lock(); messageStatusLock.readLock().lock();
@@ -454,7 +451,7 @@ DatabaseCleaner.Callback {
try { try {
if(!db.containsContact(txn, c)) if(!db.containsContact(txn, c))
throw new NoSuchContactException(); throw new NoSuchContactException();
acked = db.getBatchesToAck(txn, c, maxBatches); acked = db.getMessagesToAck(txn, c, maxMessages);
db.commitTransaction(txn); db.commitTransaction(txn);
} catch(DbException e) { } catch(DbException e) {
db.abortTransaction(txn); db.abortTransaction(txn);
@@ -469,7 +466,7 @@ DatabaseCleaner.Callback {
try { try {
T txn = db.startTransaction(); T txn = db.startTransaction();
try { try {
db.removeBatchesToAck(txn, c, acked); db.removeMessagesToAck(txn, c, acked);
db.commitTransaction(txn); db.commitTransaction(txn);
} catch(DbException e) { } catch(DbException e) {
db.abortTransaction(txn); db.abortTransaction(txn);
@@ -484,11 +481,10 @@ DatabaseCleaner.Callback {
return packetFactory.createAck(acked); return packetFactory.createAck(acked);
} }
public RawBatch generateBatch(ContactId c, int capacity) public Collection<byte[]> generateBatch(ContactId c, int maxLength)
throws DbException { throws DbException {
Collection<MessageId> ids; Collection<MessageId> ids;
List<byte[]> messages = new ArrayList<byte[]>(); List<byte[]> messages = new ArrayList<byte[]>();
RawBatch b;
// Get some sendable messages from the database // Get some sendable messages from the database
contactLock.readLock().lock(); contactLock.readLock().lock();
try { try {
@@ -502,7 +498,7 @@ DatabaseCleaner.Callback {
try { try {
if(!db.containsContact(txn, c)) if(!db.containsContact(txn, c))
throw new NoSuchContactException(); throw new NoSuchContactException();
ids = db.getSendableMessages(txn, c, capacity); ids = db.getSendableMessages(txn, c, maxLength);
for(MessageId m : ids) { for(MessageId m : ids) {
messages.add(db.getMessage(txn, m)); messages.add(db.getMessage(txn, m));
} }
@@ -518,13 +514,11 @@ DatabaseCleaner.Callback {
messageStatusLock.readLock().unlock(); messageStatusLock.readLock().unlock();
} }
if(messages.isEmpty()) return null; if(messages.isEmpty()) return null;
messages = Collections.unmodifiableList(messages);
b = packetFactory.createBatch(messages);
messageStatusLock.writeLock().lock(); messageStatusLock.writeLock().lock();
try { try {
T txn = db.startTransaction(); T txn = db.startTransaction();
try { try {
db.addOutstandingBatch(txn, c, b.getId(), ids); db.addOutstandingMessages(txn, c, ids);
db.commitTransaction(txn); db.commitTransaction(txn);
} catch(DbException e) { } catch(DbException e) {
db.abortTransaction(txn); db.abortTransaction(txn);
@@ -539,14 +533,13 @@ DatabaseCleaner.Callback {
} finally { } finally {
contactLock.readLock().unlock(); contactLock.readLock().unlock();
} }
return b; return Collections.unmodifiableList(messages);
} }
public RawBatch generateBatch(ContactId c, int capacity, public Collection<byte[]> generateBatch(ContactId c, int maxLength,
Collection<MessageId> requested) throws DbException { Collection<MessageId> requested) throws DbException {
Collection<MessageId> ids = new ArrayList<MessageId>(); Collection<MessageId> ids = new ArrayList<MessageId>();
List<byte[]> messages = new ArrayList<byte[]>(); List<byte[]> messages = new ArrayList<byte[]>();
RawBatch b;
// Get some sendable messages from the database // Get some sendable messages from the database
contactLock.readLock().lock(); contactLock.readLock().lock();
try { try {
@@ -565,10 +558,10 @@ DatabaseCleaner.Callback {
MessageId m = it.next(); MessageId m = it.next();
byte[] raw = db.getMessageIfSendable(txn, c, m); byte[] raw = db.getMessageIfSendable(txn, c, m);
if(raw != null) { if(raw != null) {
if(raw.length > capacity) break; if(raw.length > maxLength) break;
messages.add(raw); messages.add(raw);
ids.add(m); ids.add(m);
capacity -= raw.length; maxLength -= raw.length;
} }
it.remove(); it.remove();
} }
@@ -584,13 +577,11 @@ DatabaseCleaner.Callback {
messageStatusLock.readLock().unlock(); messageStatusLock.readLock().unlock();
} }
if(messages.isEmpty()) return null; if(messages.isEmpty()) return null;
messages = Collections.unmodifiableList(messages);
b = packetFactory.createBatch(messages);
messageStatusLock.writeLock().lock(); messageStatusLock.writeLock().lock();
try { try {
T txn = db.startTransaction(); T txn = db.startTransaction();
try { try {
db.addOutstandingBatch(txn, c, b.getId(), ids); db.addOutstandingMessages(txn, c, ids);
db.commitTransaction(txn); db.commitTransaction(txn);
} catch(DbException e) { } catch(DbException e) {
db.abortTransaction(txn); db.abortTransaction(txn);
@@ -605,7 +596,7 @@ DatabaseCleaner.Callback {
} finally { } finally {
contactLock.readLock().unlock(); contactLock.readLock().unlock();
} }
return b; return Collections.unmodifiableList(messages);
} }
public Offer generateOffer(ContactId c, int maxMessages) public Offer generateOffer(ContactId c, int maxMessages)
@@ -1057,12 +1048,12 @@ DatabaseCleaner.Callback {
try { try {
if(!db.containsContact(txn, c)) if(!db.containsContact(txn, c))
throw new NoSuchContactException(); throw new NoSuchContactException();
Collection<BatchId> acks = a.getBatchIds(); // Mark all acked messages as seen
// Mark all messages in acked batches as seen db.removeAckedMessages(txn, c, a.getMessageIds());
for(BatchId b : acks) db.removeAckedBatch(txn, c, b); // Find any lost messages that need to be retransmitted
// Find any lost batches that need to be retransmitted // FIXME: Merge these methods
Collection<BatchId> lost = db.getLostBatches(txn, c); Collection<MessageId> lost = db.getLostMessages(txn, c);
for(BatchId b : lost) db.removeLostBatch(txn, c, b); if(!lost.isEmpty()) db.removeLostMessages(txn, c, lost);
db.commitTransaction(txn); db.commitTransaction(txn);
} catch(DbException e) { } catch(DbException e) {
db.abortTransaction(txn); db.abortTransaction(txn);
@@ -1079,8 +1070,8 @@ DatabaseCleaner.Callback {
} }
} }
public void receiveBatch(ContactId c, Batch b) throws DbException { public void receiveMessage(ContactId c, Message m) throws DbException {
boolean anyAdded = false; boolean added = false;
contactLock.readLock().lock(); contactLock.readLock().lock();
try { try {
messageLock.writeLock().lock(); messageLock.writeLock().lock();
@@ -1093,8 +1084,8 @@ DatabaseCleaner.Callback {
try { try {
if(!db.containsContact(txn, c)) if(!db.containsContact(txn, c))
throw new NoSuchContactException(); throw new NoSuchContactException();
anyAdded = storeMessages(txn, c, b.getMessages()); added = storeMessage(txn, c, m);
db.addBatchToAck(txn, c, b.getId()); db.addMessageToAck(txn, c, m.getId());
db.commitTransaction(txn); db.commitTransaction(txn);
} catch(DbException e) { } catch(DbException e) {
db.abortTransaction(txn); db.abortTransaction(txn);
@@ -1113,32 +1104,24 @@ DatabaseCleaner.Callback {
contactLock.readLock().unlock(); contactLock.readLock().unlock();
} }
// Call the listeners outside the lock // Call the listeners outside the lock
callListeners(new BatchReceivedEvent()); callListeners(new MessageReceivedEvent());
if(anyAdded) callListeners(new MessagesAddedEvent()); if(added) callListeners(new MessageAddedEvent());
} }
/** /**
* Attempts to store a collection of messages received from the given * Attempts to store a message received from the given contact, and returns
* contact, and returns true if any were stored. * true if it was stored.
* <p> * <p>
* Locking: contact read, message write, messageStatus write, * Locking: contact read, message write, messageStatus write,
* subscription read. * subscription read.
*/ */
private boolean storeMessages(T txn, ContactId c, private boolean storeMessage(T txn, ContactId c, Message m)
Collection<Message> messages) throws DbException { throws DbException {
boolean anyStored = false; GroupId g = m.getGroup();
for(Message m : messages) { if(g == null) return storePrivateMessage(txn, m, c, true);
GroupId g = m.getGroup(); if(!db.containsVisibleSubscription(txn, g, c, m.getTimestamp()))
if(g == null) { return false;
if(storePrivateMessage(txn, m, c, true)) anyStored = true; return storeGroupMessage(txn, m, c);
} else {
long timestamp = m.getTimestamp();
if(db.containsVisibleSubscription(txn, g, c, timestamp)) {
if(storeGroupMessage(txn, m, c)) anyStored = true;
}
}
}
return anyStored;
} }
public Request receiveOffer(ContactId c, Offer o) throws DbException { public Request receiveOffer(ContactId c, Offer o) throws DbException {

View File

@@ -42,12 +42,6 @@ interface DatabaseConstants {
*/ */
long EXPIRY_MODULUS = 60L * 60L * 1000L; // 1 hour long EXPIRY_MODULUS = 60L * 60L * 1000L; // 1 hour
/**
* A batch sent to a contact is considered lost when this many more
* recently sent batches have been acknowledged.
*/
int RETRANSMIT_THRESHOLD = 5;
/** /**
* The time in milliseconds after which a subscription or transport update * The time in milliseconds after which a subscription or transport update
* should be sent to a contact even if no changes have occurred. * should be sent to a contact even if no changes have occurred.

View File

@@ -3,7 +3,6 @@ package net.sf.briar.db;
import static java.util.logging.Level.INFO; import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static net.sf.briar.db.DatabaseConstants.EXPIRY_MODULUS; import static net.sf.briar.db.DatabaseConstants.EXPIRY_MODULUS;
import static net.sf.briar.db.DatabaseConstants.RETRANSMIT_THRESHOLD;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
@@ -33,7 +32,6 @@ import net.sf.briar.api.db.DbClosedException;
import net.sf.briar.api.db.DbException; import net.sf.briar.api.db.DbException;
import net.sf.briar.api.db.MessageHeader; import net.sf.briar.api.db.MessageHeader;
import net.sf.briar.api.protocol.AuthorId; import net.sf.briar.api.protocol.AuthorId;
import net.sf.briar.api.protocol.BatchId;
import net.sf.briar.api.protocol.Group; import net.sf.briar.api.protocol.Group;
import net.sf.briar.api.protocol.GroupId; import net.sf.briar.api.protocol.GroupId;
import net.sf.briar.api.protocol.Message; import net.sf.briar.api.protocol.Message;
@@ -112,11 +110,11 @@ abstract class JdbcDatabase implements Database<Connection> {
private static final String INDEX_VISIBILITIES_BY_NEXT = private static final String INDEX_VISIBILITIES_BY_NEXT =
"CREATE INDEX visibilitiesByNext on visibilities (nextId)"; "CREATE INDEX visibilitiesByNext on visibilities (nextId)";
private static final String CREATE_BATCHES_TO_ACK = private static final String CREATE_MESSAGES_TO_ACK =
"CREATE TABLE batchesToAck" "CREATE TABLE messagesToAck"
+ " (batchId HASH NOT NULL," + " (messageId HASH NOT NULL,"
+ " contactId INT NOT NULL," + " contactId INT NOT NULL,"
+ " PRIMARY KEY (batchId, contactId)," + " PRIMARY KEY (messageId, contactId),"
+ " FOREIGN KEY (contactId) REFERENCES contacts (contactId)" + " FOREIGN KEY (contactId) REFERENCES contacts (contactId)"
+ " ON DELETE CASCADE)"; + " ON DELETE CASCADE)";
@@ -131,32 +129,6 @@ abstract class JdbcDatabase implements Database<Connection> {
+ " FOREIGN KEY (contactId) REFERENCES contacts (contactId)" + " FOREIGN KEY (contactId) REFERENCES contacts (contactId)"
+ " ON DELETE CASCADE)"; + " ON DELETE CASCADE)";
private static final String CREATE_OUTSTANDING_BATCHES =
"CREATE TABLE outstandingBatches"
+ " (batchId HASH NOT NULL,"
+ " contactId INT NOT NULL,"
+ " timestamp BIGINT NOT NULL,"
+ " passover INT NOT NULL,"
+ " PRIMARY KEY (batchId, contactId),"
+ " FOREIGN KEY (contactId) REFERENCES contacts (contactId)"
+ " ON DELETE CASCADE)";
private static final String CREATE_OUTSTANDING_MESSAGES =
"CREATE TABLE outstandingMessages"
+ " (batchId HASH NOT NULL,"
+ " contactId INT NOT NULL,"
+ " messageId HASH NOT NULL,"
+ " PRIMARY KEY (batchId, contactId, messageId),"
+ " FOREIGN KEY (batchId, contactId)"
+ " REFERENCES outstandingBatches (batchId, contactId)"
+ " ON DELETE CASCADE,"
+ " FOREIGN KEY (messageId) REFERENCES messages (messageId)"
+ " ON DELETE CASCADE)";
private static final String INDEX_OUTSTANDING_MESSAGES_BY_BATCH =
"CREATE INDEX outstandingMessagesByBatch"
+ " ON outstandingMessages (batchId)";
private static final String CREATE_RATINGS = private static final String CREATE_RATINGS =
"CREATE TABLE ratings" "CREATE TABLE ratings"
+ " (authorId HASH NOT NULL," + " (authorId HASH NOT NULL,"
@@ -322,11 +294,8 @@ abstract class JdbcDatabase implements Database<Connection> {
s.executeUpdate(insertTypeNames(CREATE_VISIBILITIES)); s.executeUpdate(insertTypeNames(CREATE_VISIBILITIES));
s.executeUpdate(INDEX_VISIBILITIES_BY_GROUP); s.executeUpdate(INDEX_VISIBILITIES_BY_GROUP);
s.executeUpdate(INDEX_VISIBILITIES_BY_NEXT); s.executeUpdate(INDEX_VISIBILITIES_BY_NEXT);
s.executeUpdate(insertTypeNames(CREATE_BATCHES_TO_ACK)); s.executeUpdate(insertTypeNames(CREATE_MESSAGES_TO_ACK));
s.executeUpdate(insertTypeNames(CREATE_CONTACT_SUBSCRIPTIONS)); s.executeUpdate(insertTypeNames(CREATE_CONTACT_SUBSCRIPTIONS));
s.executeUpdate(insertTypeNames(CREATE_OUTSTANDING_BATCHES));
s.executeUpdate(insertTypeNames(CREATE_OUTSTANDING_MESSAGES));
s.executeUpdate(INDEX_OUTSTANDING_MESSAGES_BY_BATCH);
s.executeUpdate(insertTypeNames(CREATE_RATINGS)); s.executeUpdate(insertTypeNames(CREATE_RATINGS));
s.executeUpdate(insertTypeNames(CREATE_STATUSES)); s.executeUpdate(insertTypeNames(CREATE_STATUSES));
s.executeUpdate(INDEX_STATUSES_BY_MESSAGE); s.executeUpdate(INDEX_STATUSES_BY_MESSAGE);
@@ -452,37 +421,6 @@ abstract class JdbcDatabase implements Database<Connection> {
if(interrupted) Thread.currentThread().interrupt(); if(interrupted) Thread.currentThread().interrupt();
} }
public void addBatchToAck(Connection txn, ContactId c, BatchId b)
throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT NULL FROM batchesToAck"
+ " WHERE batchId = ? AND contactId = ?";
ps = txn.prepareStatement(sql);
ps.setBytes(1, b.getBytes());
ps.setInt(2, c.getInt());
rs = ps.executeQuery();
boolean found = rs.next();
if(rs.next()) throw new DbStateException();
rs.close();
ps.close();
if(found) return;
sql = "INSERT INTO batchesToAck (batchId, contactId)"
+ " VALUES (?, ?)";
ps = txn.prepareStatement(sql);
ps.setBytes(1, b.getBytes());
ps.setInt(2, c.getInt());
int affected = ps.executeUpdate();
if(affected != 1) throw new DbStateException();
ps.close();
} catch(SQLException e) {
tryToClose(rs);
tryToClose(ps);
throw new DbException(e);
}
}
public ContactId addContact(Connection txn) throws DbException { public ContactId addContact(Connection txn) throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;
ResultSet rs = null; ResultSet rs = null;
@@ -596,42 +534,30 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
} }
public void addOutstandingBatch(Connection txn, ContactId c, BatchId b, public void addMessageToAck(Connection txn, ContactId c, MessageId m)
throws DbException {
PreparedStatement ps = null;
try {
String sql = "INSERT INTO messagesToAck (messageId, contactId)"
+ " VALUES (?, ?)";
ps = txn.prepareStatement(sql);
ps.setBytes(1, m.getBytes());
ps.setInt(2, c.getInt());
int affected = ps.executeUpdate();
if(affected > 1) throw new DbStateException();
ps.close();
} catch(SQLException e) {
tryToClose(ps);
throw new DbException(e);
}
}
public void addOutstandingMessages(Connection txn, ContactId c,
Collection<MessageId> sent) throws DbException { Collection<MessageId> sent) throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;
ResultSet rs = null;
try { try {
// Create an outstanding batch row // Set the status of each message to SENT if it's currently NEW
String sql = "INSERT INTO outstandingBatches" String sql = "UPDATE statuses SET status = ?"
+ " (batchId, contactId, timestamp, passover)"
+ " VALUES (?, ?, ?, ZERO())";
ps = txn.prepareStatement(sql);
ps.setBytes(1, b.getBytes());
ps.setInt(2, c.getInt());
ps.setLong(3, clock.currentTimeMillis());
int affected = ps.executeUpdate();
if(affected != 1) throw new DbStateException();
ps.close();
// Create an outstanding message row for each message in the batch
sql = "INSERT INTO outstandingMessages"
+ " (batchId, contactId, messageId)"
+ " VALUES (?, ?, ?)";
ps = txn.prepareStatement(sql);
ps.setBytes(1, b.getBytes());
ps.setInt(2, c.getInt());
for(MessageId m : sent) {
ps.setBytes(3, m.getBytes());
ps.addBatch();
}
int[] batchAffected = ps.executeBatch();
if(batchAffected.length != sent.size())
throw new DbStateException();
for(int i = 0; i < batchAffected.length; i++) {
if(batchAffected[i] != 1) throw new DbStateException();
}
ps.close();
// Set the status of each message in the batch to SENT
sql = "UPDATE statuses SET status = ?"
+ " WHERE messageId = ? AND contactId = ? AND status = ?"; + " WHERE messageId = ? AND contactId = ? AND status = ?";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setShort(1, (short) Status.SENT.ordinal()); ps.setShort(1, (short) Status.SENT.ordinal());
@@ -641,7 +567,7 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.setBytes(2, m.getBytes()); ps.setBytes(2, m.getBytes());
ps.addBatch(); ps.addBatch();
} }
batchAffected = ps.executeBatch(); int[] batchAffected = ps.executeBatch();
if(batchAffected.length != sent.size()) if(batchAffected.length != sent.size())
throw new DbStateException(); throw new DbStateException();
for(int i = 0; i < batchAffected.length; i++) { for(int i = 0; i < batchAffected.length; i++) {
@@ -649,7 +575,6 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
ps.close(); ps.close();
} catch(SQLException e) { } catch(SQLException e) {
tryToClose(rs);
tryToClose(ps); tryToClose(ps);
throw new DbException(e); throw new DbException(e);
} }
@@ -1006,30 +931,6 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
} }
public Collection<BatchId> getBatchesToAck(Connection txn, ContactId c,
int maxBatches) throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT batchId FROM batchesToAck"
+ " WHERE contactId = ?"
+ " LIMIT ?";
ps = txn.prepareStatement(sql);
ps.setInt(1, c.getInt());
ps.setInt(2, maxBatches);
rs = ps.executeQuery();
List<BatchId> ids = new ArrayList<BatchId>();
while(rs.next()) ids.add(new BatchId(rs.getBytes(1)));
rs.close();
ps.close();
return Collections.unmodifiableList(ids);
} catch(SQLException e) {
tryToClose(rs);
tryToClose(ps);
throw new DbException(e);
}
}
public TransportConfig getConfig(Connection txn, TransportId t) public TransportConfig getConfig(Connection txn, TransportId t)
throws DbException { throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;
@@ -1216,27 +1117,10 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
} }
public Collection<BatchId> getLostBatches(Connection txn, ContactId c) public Collection<MessageId> getLostMessages(Connection txn, ContactId c)
throws DbException { throws DbException {
PreparedStatement ps = null; // FIXME: Retransmission
ResultSet rs = null; return Collections.emptyList();
try {
String sql = "SELECT batchId FROM outstandingBatches"
+ " WHERE contactId = ? AND passover >= ?";
ps = txn.prepareStatement(sql);
ps.setInt(1, c.getInt());
ps.setInt(2, RETRANSMIT_THRESHOLD);
rs = ps.executeQuery();
List<BatchId> ids = new ArrayList<BatchId>();
while(rs.next()) ids.add(new BatchId(rs.getBytes(1)));
rs.close();
ps.close();
return Collections.unmodifiableList(ids);
} catch(SQLException e) {
tryToClose(rs);
tryToClose(ps);
throw new DbException(e);
}
} }
public byte[] getMessage(Connection txn, MessageId m) throws DbException { public byte[] getMessage(Connection txn, MessageId m) throws DbException {
@@ -1411,6 +1295,30 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
} }
public Collection<MessageId> getMessagesToAck(Connection txn, ContactId c,
int maxMessages) throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT messageId FROM messagesToAck"
+ " WHERE contactId = ?"
+ " LIMIT ?";
ps = txn.prepareStatement(sql);
ps.setInt(1, c.getInt());
ps.setInt(2, maxMessages);
rs = ps.executeQuery();
List<MessageId> ids = new ArrayList<MessageId>();
while(rs.next()) ids.add(new MessageId(rs.getBytes(1)));
rs.close();
ps.close();
return Collections.unmodifiableList(ids);
} catch(SQLException e) {
tryToClose(rs);
tryToClose(ps);
throw new DbException(e);
}
}
public int getNumberOfSendableChildren(Connection txn, MessageId m) public int getNumberOfSendableChildren(Connection txn, MessageId m)
throws DbException { throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;
@@ -1670,7 +1578,7 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
public Collection<MessageId> getSendableMessages(Connection txn, public Collection<MessageId> getSendableMessages(Connection txn,
ContactId c, int capacity) throws DbException { ContactId c, int maxLength) throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;
ResultSet rs = null; ResultSet rs = null;
try { try {
@@ -1688,13 +1596,13 @@ abstract class JdbcDatabase implements Database<Connection> {
int total = 0; int total = 0;
while(rs.next()) { while(rs.next()) {
int length = rs.getInt(1); int length = rs.getInt(1);
if(total + length > capacity) break; if(total + length > maxLength) break;
ids.add(new MessageId(rs.getBytes(2))); ids.add(new MessageId(rs.getBytes(2)));
total += length; total += length;
} }
rs.close(); rs.close();
ps.close(); ps.close();
if(total == capacity) return Collections.unmodifiableList(ids); if(total == maxLength) return Collections.unmodifiableList(ids);
// Do we have any sendable group messages? // Do we have any sendable group messages?
sql = "SELECT length, m.messageId FROM messages AS m" sql = "SELECT length, m.messageId FROM messages AS m"
+ " JOIN contactSubscriptions AS cs" + " JOIN contactSubscriptions AS cs"
@@ -1719,7 +1627,7 @@ abstract class JdbcDatabase implements Database<Connection> {
rs = ps.executeQuery(); rs = ps.executeQuery();
while(rs.next()) { while(rs.next()) {
int length = rs.getInt(1); int length = rs.getInt(1);
if(total + length > capacity) break; if(total + length > maxLength) break;
ids.add(new MessageId(rs.getBytes(2))); ids.add(new MessageId(rs.getBytes(2)));
total += length; total += length;
} }
@@ -2067,99 +1975,52 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
} }
public void removeAckedBatch(Connection txn, ContactId c, BatchId b) public void removeAckedMessages(Connection txn, ContactId c,
throws DbException { Collection<MessageId> acked) throws DbException {
PreparedStatement ps = null; setStatus(txn, c, acked, Status.SEEN);
ResultSet rs = null;
try {
String sql = "SELECT timestamp FROM outstandingBatches"
+ " WHERE contactId = ? AND batchId = ?";
ps = txn.prepareStatement(sql);
ps.setInt(1, c.getInt());
ps.setBytes(2, b.getBytes());
rs = ps.executeQuery();
if(!rs.next()) throw new DbStateException();
long timestamp = rs.getLong(1);
if(rs.next()) throw new DbStateException();
rs.close();
ps.close();
// Increment the passover count of all older outstanding batches
sql = "UPDATE outstandingBatches SET passover = passover + ?"
+ " WHERE contactId = ? AND timestamp < ?";
ps = txn.prepareStatement(sql);
ps.setInt(1, 1);
ps.setInt(2, c.getInt());
ps.setLong(3, timestamp);
ps.executeUpdate();
ps.close();
} catch(SQLException e) {
tryToClose(rs);
tryToClose(ps);
throw new DbException(e);
}
removeBatch(txn, c, b, Status.SEEN);
} }
private void removeBatch(Connection txn, ContactId c, BatchId b, private void setStatus(Connection txn, ContactId c,
Status newStatus) throws DbException { Collection<MessageId> ids, Status newStatus) throws DbException {
PreparedStatement ps = null, ps1 = null; PreparedStatement ps = null;
ResultSet rs = null;
try { try {
String sql = "SELECT messageId FROM outstandingMessages" // Set the status of each message if it's currently SENT
+ " WHERE contactId = ? AND batchId = ?"; String sql = "UPDATE statuses SET status = ?"
ps = txn.prepareStatement(sql);
ps.setInt(1, c.getInt());
ps.setBytes(2, b.getBytes());
rs = ps.executeQuery();
sql = "UPDATE statuses SET status = ?"
+ " WHERE messageId = ? AND contactId = ? AND status = ?"; + " WHERE messageId = ? AND contactId = ? AND status = ?";
ps1 = txn.prepareStatement(sql);
ps1.setShort(1, (short) newStatus.ordinal());
ps1.setInt(3, c.getInt());
ps1.setShort(4, (short) Status.SENT.ordinal());
int messages = 0;
while(rs.next()) {
messages++;
ps1.setBytes(2, rs.getBytes(1));
ps1.addBatch();
}
rs.close();
ps.close();
int[] batchAffected = ps1.executeBatch();
if(batchAffected.length != messages) throw new DbStateException();
for(int i = 0; i < batchAffected.length; i++) {
if(batchAffected[i] > 1) throw new DbStateException();
}
ps1.close();
// Cascade on delete
sql = "DELETE FROM outstandingBatches WHERE batchId = ?";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setBytes(1, b.getBytes()); ps.setShort(1, (short) newStatus.ordinal());
int affected = ps.executeUpdate(); ps.setInt(3, c.getInt());
if(affected > 1) throw new DbStateException(); ps.setShort(4, (short) Status.SENT.ordinal());
ps.close(); for(MessageId m : ids) {
} catch(SQLException e) { ps.setBytes(2, m.getBytes());
tryToClose(rs);
tryToClose(ps);
tryToClose(ps1);
throw new DbException(e);
}
}
public void removeBatchesToAck(Connection txn, ContactId c,
Collection<BatchId> sent) throws DbException {
PreparedStatement ps = null;
try {
String sql = "DELETE FROM batchesToAck"
+ " WHERE contactId = ? and batchId = ?";
ps = txn.prepareStatement(sql);
ps.setInt(1, c.getInt());
for(BatchId b : sent) {
ps.setBytes(2, b.getBytes());
ps.addBatch(); ps.addBatch();
} }
int[] batchAffected = ps.executeBatch(); int[] batchAffected = ps.executeBatch();
if(batchAffected.length != sent.size()) if(batchAffected.length != ids.size()) throw new DbStateException();
for(int i = 0; i < batchAffected.length; i++) {
if(batchAffected[i] > 1) throw new DbStateException();
}
ps.close();
} catch(SQLException e) {
tryToClose(ps);
throw new DbException(e);
}
}
public void removeMessagesToAck(Connection txn, ContactId c,
Collection<MessageId> acked) throws DbException {
PreparedStatement ps = null;
try {
String sql = "DELETE FROM messagesToAck"
+ " WHERE contactId = ? AND messageId = ?";
ps = txn.prepareStatement(sql);
ps.setInt(1, c.getInt());
for(MessageId m : acked) {
ps.setBytes(2, m.getBytes());
ps.addBatch();
}
int[] batchAffected = ps.executeBatch();
if(batchAffected.length != acked.size())
throw new DbStateException(); throw new DbStateException();
for(int i = 0; i < batchAffected.length; i++) { for(int i = 0; i < batchAffected.length; i++) {
if(batchAffected[i] != 1) throw new DbStateException(); if(batchAffected[i] != 1) throw new DbStateException();
@@ -2187,9 +2048,9 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
} }
public void removeLostBatch(Connection txn, ContactId c, BatchId b) public void removeLostMessages(Connection txn, ContactId c,
throws DbException { Collection<MessageId> lost) throws DbException {
removeBatch(txn, c, b, Status.NEW); setStatus(txn, c, lost, Status.NEW);
} }
public void removeMessage(Connection txn, MessageId m) throws DbException { public void removeMessage(Connection txn, MessageId m) throws DbException {

View File

@@ -3,17 +3,17 @@ package net.sf.briar.protocol;
import java.util.Collection; import java.util.Collection;
import net.sf.briar.api.protocol.Ack; import net.sf.briar.api.protocol.Ack;
import net.sf.briar.api.protocol.BatchId; import net.sf.briar.api.protocol.MessageId;
class AckImpl implements Ack { class AckImpl implements Ack {
private final Collection<BatchId> acked; private final Collection<MessageId> acked;
AckImpl(Collection<BatchId> acked) { AckImpl(Collection<MessageId> acked) {
this.acked = acked; this.acked = acked;
} }
public Collection<BatchId> getBatchIds() { public Collection<MessageId> getMessageIds() {
return acked; return acked;
} }
} }

View File

@@ -10,7 +10,7 @@ import java.util.List;
import net.sf.briar.api.Bytes; import net.sf.briar.api.Bytes;
import net.sf.briar.api.FormatException; import net.sf.briar.api.FormatException;
import net.sf.briar.api.protocol.Ack; import net.sf.briar.api.protocol.Ack;
import net.sf.briar.api.protocol.BatchId; import net.sf.briar.api.protocol.MessageId;
import net.sf.briar.api.protocol.PacketFactory; import net.sf.briar.api.protocol.PacketFactory;
import net.sf.briar.api.protocol.Types; import net.sf.briar.api.protocol.Types;
import net.sf.briar.api.protocol.UniqueId; import net.sf.briar.api.protocol.UniqueId;
@@ -38,14 +38,14 @@ class AckReader implements StructReader<Ack> {
r.resetMaxBytesLength(); r.resetMaxBytesLength();
r.removeConsumer(counting); r.removeConsumer(counting);
if(raw.isEmpty()) throw new FormatException(); if(raw.isEmpty()) throw new FormatException();
// Convert the byte arrays to batch IDs // Convert the byte arrays to message IDs
List<BatchId> batches = new ArrayList<BatchId>(); List<MessageId> acked = new ArrayList<MessageId>();
for(Bytes b : raw) { for(Bytes b : raw) {
if(b.getBytes().length != UniqueId.LENGTH) if(b.getBytes().length != UniqueId.LENGTH)
throw new FormatException(); throw new FormatException();
batches.add(new BatchId(b.getBytes())); acked.add(new MessageId(b.getBytes()));
} }
// Build and return the ack // Build and return the ack
return packetFactory.createAck(Collections.unmodifiableList(batches)); return packetFactory.createAck(Collections.unmodifiableList(acked));
} }
} }

View File

@@ -1,27 +0,0 @@
package net.sf.briar.protocol;
import java.util.Collection;
import net.sf.briar.api.protocol.Batch;
import net.sf.briar.api.protocol.BatchId;
import net.sf.briar.api.protocol.Message;
/** A simple in-memory implementation of a batch. */
class BatchImpl implements Batch {
private final BatchId id;
private final Collection<Message> messages;
BatchImpl(BatchId id, Collection<Message> messages) {
this.id = id;
this.messages = messages;
}
public BatchId getId() {
return id;
}
public Collection<Message> getMessages() {
return messages;
}
}

View File

@@ -1,41 +0,0 @@
package net.sf.briar.protocol;
import static net.sf.briar.api.protocol.ProtocolConstants.MAX_PACKET_LENGTH;
import java.io.IOException;
import java.util.List;
import net.sf.briar.api.FormatException;
import net.sf.briar.api.protocol.Types;
import net.sf.briar.api.protocol.UnverifiedBatch;
import net.sf.briar.api.serial.Consumer;
import net.sf.briar.api.serial.CountingConsumer;
import net.sf.briar.api.serial.StructReader;
import net.sf.briar.api.serial.Reader;
class BatchReader implements StructReader<UnverifiedBatch> {
private final StructReader<UnverifiedMessage> messageReader;
private final UnverifiedBatchFactory batchFactory;
BatchReader(StructReader<UnverifiedMessage> messageReader,
UnverifiedBatchFactory batchFactory) {
this.messageReader = messageReader;
this.batchFactory = batchFactory;
}
public UnverifiedBatch readStruct(Reader r) throws IOException {
// Initialise the consumer
Consumer counting = new CountingConsumer(MAX_PACKET_LENGTH);
// Read the data
r.addConsumer(counting);
r.readStructId(Types.BATCH);
r.addStructReader(Types.MESSAGE, messageReader);
List<UnverifiedMessage> messages = r.readList(UnverifiedMessage.class);
r.removeStructReader(Types.MESSAGE);
r.removeConsumer(counting);
if(messages.isEmpty()) throw new FormatException();
// Build and return the batch
return batchFactory.createUnverifiedBatch( messages);
}
}

View File

@@ -14,10 +14,11 @@ import net.sf.briar.api.protocol.Group;
import net.sf.briar.api.protocol.MessageId; import net.sf.briar.api.protocol.MessageId;
import net.sf.briar.api.protocol.Types; import net.sf.briar.api.protocol.Types;
import net.sf.briar.api.protocol.UniqueId; import net.sf.briar.api.protocol.UniqueId;
import net.sf.briar.api.protocol.UnverifiedMessage;
import net.sf.briar.api.serial.CopyingConsumer; import net.sf.briar.api.serial.CopyingConsumer;
import net.sf.briar.api.serial.CountingConsumer; import net.sf.briar.api.serial.CountingConsumer;
import net.sf.briar.api.serial.StructReader;
import net.sf.briar.api.serial.Reader; import net.sf.briar.api.serial.Reader;
import net.sf.briar.api.serial.StructReader;
class MessageReader implements StructReader<UnverifiedMessage> { class MessageReader implements StructReader<UnverifiedMessage> {

View File

@@ -3,63 +3,44 @@ package net.sf.briar.protocol;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
import java.security.PublicKey; import java.security.PublicKey;
import java.security.Signature; import java.security.Signature;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import net.sf.briar.api.crypto.CryptoComponent; import net.sf.briar.api.crypto.CryptoComponent;
import net.sf.briar.api.crypto.KeyParser; import net.sf.briar.api.crypto.KeyParser;
import net.sf.briar.api.crypto.MessageDigest; import net.sf.briar.api.crypto.MessageDigest;
import net.sf.briar.api.protocol.Author; import net.sf.briar.api.protocol.Author;
import net.sf.briar.api.protocol.AuthorId; import net.sf.briar.api.protocol.AuthorId;
import net.sf.briar.api.protocol.Batch;
import net.sf.briar.api.protocol.BatchId;
import net.sf.briar.api.protocol.Group; import net.sf.briar.api.protocol.Group;
import net.sf.briar.api.protocol.GroupId; import net.sf.briar.api.protocol.GroupId;
import net.sf.briar.api.protocol.Message; import net.sf.briar.api.protocol.Message;
import net.sf.briar.api.protocol.MessageId; import net.sf.briar.api.protocol.MessageId;
import net.sf.briar.api.protocol.UnverifiedBatch; import net.sf.briar.api.protocol.MessageVerifier;
import net.sf.briar.api.protocol.UnverifiedMessage;
class UnverifiedBatchImpl implements UnverifiedBatch { import com.google.inject.Inject;
class MessageVerifierImpl implements MessageVerifier {
private final CryptoComponent crypto; private final CryptoComponent crypto;
private final Collection<UnverifiedMessage> messages; private final KeyParser keyParser;
private final MessageDigest batchDigest, messageDigest;
// Initialise lazily - the batch may contain unsigned messages @Inject
private KeyParser keyParser = null; MessageVerifierImpl(CryptoComponent crypto) {
private Signature signature = null;
UnverifiedBatchImpl(CryptoComponent crypto,
Collection<UnverifiedMessage> messages) {
this.crypto = crypto; this.crypto = crypto;
this.messages = messages; keyParser = crypto.getSignatureKeyParser();
batchDigest = crypto.getMessageDigest();
messageDigest = crypto.getMessageDigest();
} }
public Batch verify() throws GeneralSecurityException { public Message verifyMessage(UnverifiedMessage m)
List<Message> verified = new ArrayList<Message>(); throws GeneralSecurityException {
for(UnverifiedMessage m : messages) verified.add(verify(m)); MessageDigest messageDigest = crypto.getMessageDigest();
BatchId id = new BatchId(batchDigest.digest()); Signature signature = crypto.getSignature();
return new BatchImpl(id, Collections.unmodifiableList(verified));
}
private Message verify(UnverifiedMessage m)
throws GeneralSecurityException {
// The batch ID is the hash of the concatenated messages
byte[] raw = m.getRaw();
batchDigest.update(raw);
// Hash the message, including the signatures, to get the message ID // Hash the message, including the signatures, to get the message ID
byte[] raw = m.getSerialised();
messageDigest.update(raw); messageDigest.update(raw);
MessageId id = new MessageId(messageDigest.digest()); MessageId id = new MessageId(messageDigest.digest());
// Verify the author's signature, if there is one // Verify the author's signature, if there is one
Author author = m.getAuthor(); Author author = m.getAuthor();
if(author != null) { if(author != null) {
if(keyParser == null) keyParser = crypto.getSignatureKeyParser();
PublicKey k = keyParser.parsePublicKey(author.getPublicKey()); PublicKey k = keyParser.parsePublicKey(author.getPublicKey());
if(signature == null) signature = crypto.getSignature();
signature.initVerify(k); signature.initVerify(k);
signature.update(raw, 0, m.getLengthSignedByAuthor()); signature.update(raw, 0, m.getLengthSignedByAuthor());
if(!signature.verify(m.getAuthorSignature())) if(!signature.verify(m.getAuthorSignature()))
@@ -68,9 +49,7 @@ class UnverifiedBatchImpl implements UnverifiedBatch {
// Verify the group's signature, if there is one // Verify the group's signature, if there is one
Group group = m.getGroup(); Group group = m.getGroup();
if(group != null && group.getPublicKey() != null) { if(group != null && group.getPublicKey() != null) {
if(keyParser == null) keyParser = crypto.getSignatureKeyParser();
PublicKey k = keyParser.parsePublicKey(group.getPublicKey()); PublicKey k = keyParser.parsePublicKey(group.getPublicKey());
if(signature == null) signature = crypto.getSignature();
signature.initVerify(k); signature.initVerify(k);
signature.update(raw, 0, m.getLengthSignedByGroup()); signature.update(raw, 0, m.getLengthSignedByGroup());
if(!signature.verify(m.getGroupSignature())) if(!signature.verify(m.getGroupSignature()))

View File

@@ -4,42 +4,23 @@ import java.util.BitSet;
import java.util.Collection; import java.util.Collection;
import java.util.Map; import java.util.Map;
import net.sf.briar.api.crypto.CryptoComponent;
import net.sf.briar.api.crypto.MessageDigest;
import net.sf.briar.api.protocol.Ack; import net.sf.briar.api.protocol.Ack;
import net.sf.briar.api.protocol.BatchId;
import net.sf.briar.api.protocol.Group; import net.sf.briar.api.protocol.Group;
import net.sf.briar.api.protocol.GroupId; import net.sf.briar.api.protocol.GroupId;
import net.sf.briar.api.protocol.MessageId; import net.sf.briar.api.protocol.MessageId;
import net.sf.briar.api.protocol.Offer; import net.sf.briar.api.protocol.Offer;
import net.sf.briar.api.protocol.PacketFactory; import net.sf.briar.api.protocol.PacketFactory;
import net.sf.briar.api.protocol.RawBatch;
import net.sf.briar.api.protocol.Request; import net.sf.briar.api.protocol.Request;
import net.sf.briar.api.protocol.SubscriptionUpdate; import net.sf.briar.api.protocol.SubscriptionUpdate;
import net.sf.briar.api.protocol.Transport; import net.sf.briar.api.protocol.Transport;
import net.sf.briar.api.protocol.TransportUpdate; import net.sf.briar.api.protocol.TransportUpdate;
import com.google.inject.Inject;
class PacketFactoryImpl implements PacketFactory { class PacketFactoryImpl implements PacketFactory {
private final CryptoComponent crypto; public Ack createAck(Collection<MessageId> acked) {
@Inject
PacketFactoryImpl(CryptoComponent crypto) {
this.crypto = crypto;
}
public Ack createAck(Collection<BatchId> acked) {
return new AckImpl(acked); return new AckImpl(acked);
} }
public RawBatch createBatch(Collection<byte[]> messages) {
MessageDigest messageDigest = crypto.getMessageDigest();
for(byte[] raw : messages) messageDigest.update(raw);
return new RawBatchImpl(new BatchId(messageDigest.digest()), messages);
}
public Offer createOffer(Collection<MessageId> offered) { public Offer createOffer(Collection<MessageId> offered) {
return new OfferImpl(offered); return new OfferImpl(offered);
} }

View File

@@ -9,6 +9,7 @@ import net.sf.briar.api.protocol.AuthorFactory;
import net.sf.briar.api.protocol.Group; import net.sf.briar.api.protocol.Group;
import net.sf.briar.api.protocol.GroupFactory; import net.sf.briar.api.protocol.GroupFactory;
import net.sf.briar.api.protocol.MessageFactory; import net.sf.briar.api.protocol.MessageFactory;
import net.sf.briar.api.protocol.MessageVerifier;
import net.sf.briar.api.protocol.Offer; import net.sf.briar.api.protocol.Offer;
import net.sf.briar.api.protocol.PacketFactory; import net.sf.briar.api.protocol.PacketFactory;
import net.sf.briar.api.protocol.ProtocolReaderFactory; import net.sf.briar.api.protocol.ProtocolReaderFactory;
@@ -16,7 +17,7 @@ import net.sf.briar.api.protocol.ProtocolWriterFactory;
import net.sf.briar.api.protocol.Request; import net.sf.briar.api.protocol.Request;
import net.sf.briar.api.protocol.SubscriptionUpdate; import net.sf.briar.api.protocol.SubscriptionUpdate;
import net.sf.briar.api.protocol.TransportUpdate; import net.sf.briar.api.protocol.TransportUpdate;
import net.sf.briar.api.protocol.UnverifiedBatch; import net.sf.briar.api.protocol.UnverifiedMessage;
import net.sf.briar.api.protocol.VerificationExecutor; import net.sf.briar.api.protocol.VerificationExecutor;
import net.sf.briar.api.serial.StructReader; import net.sf.briar.api.serial.StructReader;
import net.sf.briar.util.BoundedExecutor; import net.sf.briar.util.BoundedExecutor;
@@ -46,10 +47,10 @@ public class ProtocolModule extends AbstractModule {
bind(AuthorFactory.class).to(AuthorFactoryImpl.class); bind(AuthorFactory.class).to(AuthorFactoryImpl.class);
bind(GroupFactory.class).to(GroupFactoryImpl.class); bind(GroupFactory.class).to(GroupFactoryImpl.class);
bind(MessageFactory.class).to(MessageFactoryImpl.class); bind(MessageFactory.class).to(MessageFactoryImpl.class);
bind(MessageVerifier.class).to(MessageVerifierImpl.class);
bind(PacketFactory.class).to(PacketFactoryImpl.class); bind(PacketFactory.class).to(PacketFactoryImpl.class);
bind(ProtocolReaderFactory.class).to(ProtocolReaderFactoryImpl.class); bind(ProtocolReaderFactory.class).to(ProtocolReaderFactoryImpl.class);
bind(ProtocolWriterFactory.class).to(ProtocolWriterFactoryImpl.class); bind(ProtocolWriterFactory.class).to(ProtocolWriterFactoryImpl.class);
bind(UnverifiedBatchFactory.class).to(UnverifiedBatchFactoryImpl.class);
// The executor is bounded, so tasks must be independent and short-lived // The executor is bounded, so tasks must be independent and short-lived
bind(Executor.class).annotatedWith( bind(Executor.class).annotatedWith(
VerificationExecutor.class).toInstance( VerificationExecutor.class).toInstance(
@@ -68,13 +69,6 @@ public class ProtocolModule extends AbstractModule {
return new AuthorReader(crypto, authorFactory); return new AuthorReader(crypto, authorFactory);
} }
@Provides
StructReader<UnverifiedBatch> getBatchReader(
StructReader<UnverifiedMessage> messageReader,
UnverifiedBatchFactory batchFactory) {
return new BatchReader(messageReader, batchFactory);
}
@Provides @Provides
StructReader<Group> getGroupReader(CryptoComponent crypto) { StructReader<Group> getGroupReader(CryptoComponent crypto) {
return new GroupReader(crypto); return new GroupReader(crypto);

View File

@@ -9,9 +9,9 @@ import net.sf.briar.api.protocol.ProtocolReaderFactory;
import net.sf.briar.api.protocol.Request; import net.sf.briar.api.protocol.Request;
import net.sf.briar.api.protocol.SubscriptionUpdate; import net.sf.briar.api.protocol.SubscriptionUpdate;
import net.sf.briar.api.protocol.TransportUpdate; import net.sf.briar.api.protocol.TransportUpdate;
import net.sf.briar.api.protocol.UnverifiedBatch; import net.sf.briar.api.protocol.UnverifiedMessage;
import net.sf.briar.api.serial.StructReader;
import net.sf.briar.api.serial.ReaderFactory; import net.sf.briar.api.serial.ReaderFactory;
import net.sf.briar.api.serial.StructReader;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Provider; import com.google.inject.Provider;
@@ -20,7 +20,7 @@ class ProtocolReaderFactoryImpl implements ProtocolReaderFactory {
private final ReaderFactory readerFactory; private final ReaderFactory readerFactory;
private final Provider<StructReader<Ack>> ackProvider; private final Provider<StructReader<Ack>> ackProvider;
private final Provider<StructReader<UnverifiedBatch>> batchProvider; private final Provider<StructReader<UnverifiedMessage>> messageProvider;
private final Provider<StructReader<Offer>> offerProvider; private final Provider<StructReader<Offer>> offerProvider;
private final Provider<StructReader<Request>> requestProvider; private final Provider<StructReader<Request>> requestProvider;
private final Provider<StructReader<SubscriptionUpdate>> subscriptionProvider; private final Provider<StructReader<SubscriptionUpdate>> subscriptionProvider;
@@ -29,14 +29,14 @@ class ProtocolReaderFactoryImpl implements ProtocolReaderFactory {
@Inject @Inject
ProtocolReaderFactoryImpl(ReaderFactory readerFactory, ProtocolReaderFactoryImpl(ReaderFactory readerFactory,
Provider<StructReader<Ack>> ackProvider, Provider<StructReader<Ack>> ackProvider,
Provider<StructReader<UnverifiedBatch>> batchProvider, Provider<StructReader<UnverifiedMessage>> messageProvider,
Provider<StructReader<Offer>> offerProvider, Provider<StructReader<Offer>> offerProvider,
Provider<StructReader<Request>> requestProvider, Provider<StructReader<Request>> requestProvider,
Provider<StructReader<SubscriptionUpdate>> subscriptionProvider, Provider<StructReader<SubscriptionUpdate>> subscriptionProvider,
Provider<StructReader<TransportUpdate>> transportProvider) { Provider<StructReader<TransportUpdate>> transportProvider) {
this.readerFactory = readerFactory; this.readerFactory = readerFactory;
this.ackProvider = ackProvider; this.ackProvider = ackProvider;
this.batchProvider = batchProvider; this.messageProvider = messageProvider;
this.offerProvider = offerProvider; this.offerProvider = offerProvider;
this.requestProvider = requestProvider; this.requestProvider = requestProvider;
this.subscriptionProvider = subscriptionProvider; this.subscriptionProvider = subscriptionProvider;
@@ -45,7 +45,8 @@ class ProtocolReaderFactoryImpl implements ProtocolReaderFactory {
public ProtocolReader createProtocolReader(InputStream in) { public ProtocolReader createProtocolReader(InputStream in) {
return new ProtocolReaderImpl(in, readerFactory, ackProvider.get(), return new ProtocolReaderImpl(in, readerFactory, ackProvider.get(),
batchProvider.get(), offerProvider.get(), requestProvider.get(), messageProvider.get(), offerProvider.get(),
subscriptionProvider.get(), transportProvider.get()); requestProvider.get(), subscriptionProvider.get(),
transportProvider.get());
} }
} }

View File

@@ -10,10 +10,10 @@ import net.sf.briar.api.protocol.Request;
import net.sf.briar.api.protocol.SubscriptionUpdate; import net.sf.briar.api.protocol.SubscriptionUpdate;
import net.sf.briar.api.protocol.TransportUpdate; import net.sf.briar.api.protocol.TransportUpdate;
import net.sf.briar.api.protocol.Types; import net.sf.briar.api.protocol.Types;
import net.sf.briar.api.protocol.UnverifiedBatch; import net.sf.briar.api.protocol.UnverifiedMessage;
import net.sf.briar.api.serial.StructReader;
import net.sf.briar.api.serial.Reader; import net.sf.briar.api.serial.Reader;
import net.sf.briar.api.serial.ReaderFactory; import net.sf.briar.api.serial.ReaderFactory;
import net.sf.briar.api.serial.StructReader;
class ProtocolReaderImpl implements ProtocolReader { class ProtocolReaderImpl implements ProtocolReader {
@@ -21,14 +21,14 @@ class ProtocolReaderImpl implements ProtocolReader {
ProtocolReaderImpl(InputStream in, ReaderFactory readerFactory, ProtocolReaderImpl(InputStream in, ReaderFactory readerFactory,
StructReader<Ack> ackReader, StructReader<Ack> ackReader,
StructReader<UnverifiedBatch> batchReader, StructReader<UnverifiedMessage> messageReader,
StructReader<Offer> offerReader, StructReader<Offer> offerReader,
StructReader<Request> requestReader, StructReader<Request> requestReader,
StructReader<SubscriptionUpdate> subscriptionReader, StructReader<SubscriptionUpdate> subscriptionReader,
StructReader<TransportUpdate> transportReader) { StructReader<TransportUpdate> transportReader) {
reader = readerFactory.createReader(in); reader = readerFactory.createReader(in);
reader.addStructReader(Types.ACK, ackReader); reader.addStructReader(Types.ACK, ackReader);
reader.addStructReader(Types.BATCH, batchReader); reader.addStructReader(Types.MESSAGE, messageReader);
reader.addStructReader(Types.OFFER, offerReader); reader.addStructReader(Types.OFFER, offerReader);
reader.addStructReader(Types.REQUEST, requestReader); reader.addStructReader(Types.REQUEST, requestReader);
reader.addStructReader(Types.SUBSCRIPTION_UPDATE, subscriptionReader); reader.addStructReader(Types.SUBSCRIPTION_UPDATE, subscriptionReader);
@@ -47,12 +47,12 @@ class ProtocolReaderImpl implements ProtocolReader {
return reader.readStruct(Types.ACK, Ack.class); return reader.readStruct(Types.ACK, Ack.class);
} }
public boolean hasBatch() throws IOException { public boolean hasMessage() throws IOException {
return reader.hasStruct(Types.BATCH); return reader.hasStruct(Types.MESSAGE);
} }
public UnverifiedBatch readBatch() throws IOException { public UnverifiedMessage readMessage() throws IOException {
return reader.readStruct(Types.BATCH, UnverifiedBatch.class); return reader.readStruct(Types.MESSAGE, UnverifiedMessage.class);
} }
public boolean hasOffer() throws IOException { public boolean hasOffer() throws IOException {

View File

@@ -8,13 +8,11 @@ import java.util.BitSet;
import java.util.Map.Entry; import java.util.Map.Entry;
import net.sf.briar.api.protocol.Ack; import net.sf.briar.api.protocol.Ack;
import net.sf.briar.api.protocol.BatchId;
import net.sf.briar.api.protocol.Group; import net.sf.briar.api.protocol.Group;
import net.sf.briar.api.protocol.GroupId; import net.sf.briar.api.protocol.GroupId;
import net.sf.briar.api.protocol.MessageId; import net.sf.briar.api.protocol.MessageId;
import net.sf.briar.api.protocol.Offer; import net.sf.briar.api.protocol.Offer;
import net.sf.briar.api.protocol.ProtocolWriter; import net.sf.briar.api.protocol.ProtocolWriter;
import net.sf.briar.api.protocol.RawBatch;
import net.sf.briar.api.protocol.Request; import net.sf.briar.api.protocol.Request;
import net.sf.briar.api.protocol.SubscriptionUpdate; import net.sf.briar.api.protocol.SubscriptionUpdate;
import net.sf.briar.api.protocol.Transport; import net.sf.briar.api.protocol.Transport;
@@ -40,11 +38,11 @@ class ProtocolWriterImpl implements ProtocolWriter {
w = writerFactory.createWriter(out); w = writerFactory.createWriter(out);
} }
public int getMaxBatchesForAck(long capacity) { public int getMaxMessagesForAck(long capacity) {
int packet = (int) Math.min(capacity, MAX_PACKET_LENGTH); int packet = (int) Math.min(capacity, MAX_PACKET_LENGTH);
int overhead = serial.getSerialisedStructIdLength(Types.ACK) int overhead = serial.getSerialisedStructIdLength(Types.ACK)
+ serial.getSerialisedListStartLength() + serial.getSerialisedListStartLength()
+ serial.getSerialisedListEndLength(); + serial.getSerialisedListEndLength();
int idLength = serial.getSerialisedUniqueIdLength(); int idLength = serial.getSerialisedUniqueIdLength();
return (packet - overhead) / idLength; return (packet - overhead) / idLength;
} }
@@ -52,33 +50,22 @@ class ProtocolWriterImpl implements ProtocolWriter {
public int getMaxMessagesForOffer(long capacity) { public int getMaxMessagesForOffer(long capacity) {
int packet = (int) Math.min(capacity, MAX_PACKET_LENGTH); int packet = (int) Math.min(capacity, MAX_PACKET_LENGTH);
int overhead = serial.getSerialisedStructIdLength(Types.OFFER) int overhead = serial.getSerialisedStructIdLength(Types.OFFER)
+ serial.getSerialisedListStartLength() + serial.getSerialisedListStartLength()
+ serial.getSerialisedListEndLength(); + serial.getSerialisedListEndLength();
int idLength = serial.getSerialisedUniqueIdLength(); int idLength = serial.getSerialisedUniqueIdLength();
return (packet - overhead) / idLength; return (packet - overhead) / idLength;
} }
public int getMessageCapacityForBatch(long capacity) {
int packet = (int) Math.min(capacity, MAX_PACKET_LENGTH);
int overhead = serial.getSerialisedStructIdLength(Types.BATCH)
+ serial.getSerialisedListStartLength()
+ serial.getSerialisedListEndLength();
return packet - overhead;
}
public void writeAck(Ack a) throws IOException { public void writeAck(Ack a) throws IOException {
w.writeStructId(Types.ACK); w.writeStructId(Types.ACK);
w.writeListStart(); w.writeListStart();
for(BatchId b : a.getBatchIds()) w.writeBytes(b.getBytes()); for(MessageId m : a.getMessageIds()) w.writeBytes(m.getBytes());
w.writeListEnd(); w.writeListEnd();
if(flush) out.flush(); if(flush) out.flush();
} }
public void writeBatch(RawBatch b) throws IOException { public void writeMessage(byte[] raw) throws IOException {
w.writeStructId(Types.BATCH); out.write(raw);
w.writeListStart();
for(byte[] raw : b.getMessages()) out.write(raw);
w.writeListEnd();
if(flush) out.flush(); if(flush) out.flush();
} }
@@ -111,7 +98,7 @@ class ProtocolWriterImpl implements ProtocolWriter {
} }
public void writeSubscriptionUpdate(SubscriptionUpdate s) public void writeSubscriptionUpdate(SubscriptionUpdate s)
throws IOException { throws IOException {
w.writeStructId(Types.SUBSCRIPTION_UPDATE); w.writeStructId(Types.SUBSCRIPTION_UPDATE);
// Holes // Holes
w.writeMapStart(); w.writeMapStart();

View File

@@ -1,25 +0,0 @@
package net.sf.briar.protocol;
import java.util.Collection;
import net.sf.briar.api.protocol.BatchId;
import net.sf.briar.api.protocol.RawBatch;
class RawBatchImpl implements RawBatch {
private final BatchId id;
private final Collection<byte[]> messages;
RawBatchImpl(BatchId id, Collection<byte[]> messages) {
this.id = id;
this.messages = messages;
}
public BatchId getId() {
return id;
}
public Collection<byte[]> getMessages() {
return messages;
}
}

View File

@@ -1,11 +0,0 @@
package net.sf.briar.protocol;
import java.util.Collection;
import net.sf.briar.api.protocol.UnverifiedBatch;
interface UnverifiedBatchFactory {
UnverifiedBatch createUnverifiedBatch(
Collection<UnverifiedMessage> messages);
}

View File

@@ -1,23 +0,0 @@
package net.sf.briar.protocol;
import java.util.Collection;
import net.sf.briar.api.crypto.CryptoComponent;
import net.sf.briar.api.protocol.UnverifiedBatch;
import com.google.inject.Inject;
class UnverifiedBatchFactoryImpl implements UnverifiedBatchFactory {
private final CryptoComponent crypto;
@Inject
UnverifiedBatchFactoryImpl(CryptoComponent crypto) {
this.crypto = crypto;
}
public UnverifiedBatch createUnverifiedBatch(
Collection<UnverifiedMessage> messages) {
return new UnverifiedBatchImpl(crypto, messages);
}
}

View File

@@ -3,6 +3,7 @@ package net.sf.briar.protocol;
import net.sf.briar.api.protocol.Author; import net.sf.briar.api.protocol.Author;
import net.sf.briar.api.protocol.Group; import net.sf.briar.api.protocol.Group;
import net.sf.briar.api.protocol.MessageId; import net.sf.briar.api.protocol.MessageId;
import net.sf.briar.api.protocol.UnverifiedMessage;
class UnverifiedMessageImpl implements UnverifiedMessage { class UnverifiedMessageImpl implements UnverifiedMessage {
@@ -52,7 +53,7 @@ class UnverifiedMessageImpl implements UnverifiedMessage {
return timestamp; return timestamp;
} }
public byte[] getRaw() { public byte[] getSerialised() {
return raw; return raw;
} }

View File

@@ -24,28 +24,28 @@ import net.sf.briar.api.FormatException;
import net.sf.briar.api.db.DatabaseComponent; import net.sf.briar.api.db.DatabaseComponent;
import net.sf.briar.api.db.DatabaseExecutor; import net.sf.briar.api.db.DatabaseExecutor;
import net.sf.briar.api.db.DbException; import net.sf.briar.api.db.DbException;
import net.sf.briar.api.db.event.BatchReceivedEvent;
import net.sf.briar.api.db.event.ContactRemovedEvent; import net.sf.briar.api.db.event.ContactRemovedEvent;
import net.sf.briar.api.db.event.DatabaseEvent; import net.sf.briar.api.db.event.DatabaseEvent;
import net.sf.briar.api.db.event.DatabaseListener; import net.sf.briar.api.db.event.DatabaseListener;
import net.sf.briar.api.db.event.LocalTransportsUpdatedEvent; import net.sf.briar.api.db.event.LocalTransportsUpdatedEvent;
import net.sf.briar.api.db.event.MessagesAddedEvent; import net.sf.briar.api.db.event.MessageAddedEvent;
import net.sf.briar.api.db.event.MessageReceivedEvent;
import net.sf.briar.api.db.event.SubscriptionsUpdatedEvent; import net.sf.briar.api.db.event.SubscriptionsUpdatedEvent;
import net.sf.briar.api.plugins.duplex.DuplexTransportConnection; import net.sf.briar.api.plugins.duplex.DuplexTransportConnection;
import net.sf.briar.api.protocol.Ack; import net.sf.briar.api.protocol.Ack;
import net.sf.briar.api.protocol.Batch; import net.sf.briar.api.protocol.Message;
import net.sf.briar.api.protocol.MessageId; import net.sf.briar.api.protocol.MessageId;
import net.sf.briar.api.protocol.MessageVerifier;
import net.sf.briar.api.protocol.Offer; import net.sf.briar.api.protocol.Offer;
import net.sf.briar.api.protocol.ProtocolReader; import net.sf.briar.api.protocol.ProtocolReader;
import net.sf.briar.api.protocol.ProtocolReaderFactory; import net.sf.briar.api.protocol.ProtocolReaderFactory;
import net.sf.briar.api.protocol.ProtocolWriter; import net.sf.briar.api.protocol.ProtocolWriter;
import net.sf.briar.api.protocol.ProtocolWriterFactory; import net.sf.briar.api.protocol.ProtocolWriterFactory;
import net.sf.briar.api.protocol.RawBatch;
import net.sf.briar.api.protocol.Request; import net.sf.briar.api.protocol.Request;
import net.sf.briar.api.protocol.SubscriptionUpdate; import net.sf.briar.api.protocol.SubscriptionUpdate;
import net.sf.briar.api.protocol.TransportId; import net.sf.briar.api.protocol.TransportId;
import net.sf.briar.api.protocol.TransportUpdate; import net.sf.briar.api.protocol.TransportUpdate;
import net.sf.briar.api.protocol.UnverifiedBatch; import net.sf.briar.api.protocol.UnverifiedMessage;
import net.sf.briar.api.protocol.VerificationExecutor; import net.sf.briar.api.protocol.VerificationExecutor;
import net.sf.briar.api.transport.ConnectionContext; import net.sf.briar.api.transport.ConnectionContext;
import net.sf.briar.api.transport.ConnectionReader; import net.sf.briar.api.transport.ConnectionReader;
@@ -76,6 +76,7 @@ abstract class DuplexConnection implements DatabaseListener {
protected final TransportId transportId; protected final TransportId transportId;
private final Executor dbExecutor, verificationExecutor; private final Executor dbExecutor, verificationExecutor;
private final MessageVerifier messageVerifier;
private final AtomicBoolean canSendOffer, disposed; private final AtomicBoolean canSendOffer, disposed;
private final BlockingQueue<Runnable> writerTasks; private final BlockingQueue<Runnable> writerTasks;
@@ -85,7 +86,8 @@ abstract class DuplexConnection implements DatabaseListener {
DuplexConnection(@DatabaseExecutor Executor dbExecutor, DuplexConnection(@DatabaseExecutor Executor dbExecutor,
@VerificationExecutor Executor verificationExecutor, @VerificationExecutor Executor verificationExecutor,
DatabaseComponent db, ConnectionRegistry connRegistry, MessageVerifier messageVerifier, DatabaseComponent db,
ConnectionRegistry connRegistry,
ConnectionReaderFactory connReaderFactory, ConnectionReaderFactory connReaderFactory,
ConnectionWriterFactory connWriterFactory, ConnectionWriterFactory connWriterFactory,
ProtocolReaderFactory protoReaderFactory, ProtocolReaderFactory protoReaderFactory,
@@ -93,6 +95,7 @@ abstract class DuplexConnection implements DatabaseListener {
DuplexTransportConnection transport) { DuplexTransportConnection transport) {
this.dbExecutor = dbExecutor; this.dbExecutor = dbExecutor;
this.verificationExecutor = verificationExecutor; this.verificationExecutor = verificationExecutor;
this.messageVerifier = messageVerifier;
this.db = db; this.db = db;
this.connRegistry = connRegistry; this.connRegistry = connRegistry;
this.connReaderFactory = connReaderFactory; this.connReaderFactory = connReaderFactory;
@@ -115,12 +118,12 @@ abstract class DuplexConnection implements DatabaseListener {
throws IOException; throws IOException;
public void eventOccurred(DatabaseEvent e) { public void eventOccurred(DatabaseEvent e) {
if(e instanceof BatchReceivedEvent) { if(e instanceof MessageReceivedEvent) {
dbExecutor.execute(new GenerateAcks()); dbExecutor.execute(new GenerateAcks());
} else if(e instanceof ContactRemovedEvent) { } else if(e instanceof ContactRemovedEvent) {
ContactId c = ((ContactRemovedEvent) e).getContactId(); ContactId c = ((ContactRemovedEvent) e).getContactId();
if(contactId.equals(c)) dispose(false, true); if(contactId.equals(c)) dispose(false, true);
} else if(e instanceof MessagesAddedEvent) { } else if(e instanceof MessageAddedEvent) {
if(canSendOffer.getAndSet(false)) if(canSendOffer.getAndSet(false))
dbExecutor.execute(new GenerateOffer()); dbExecutor.execute(new GenerateOffer());
} else if(e instanceof SubscriptionsUpdatedEvent) { } else if(e instanceof SubscriptionsUpdatedEvent) {
@@ -142,9 +145,9 @@ abstract class DuplexConnection implements DatabaseListener {
if(reader.hasAck()) { if(reader.hasAck()) {
Ack a = reader.readAck(); Ack a = reader.readAck();
dbExecutor.execute(new ReceiveAck(a)); dbExecutor.execute(new ReceiveAck(a));
} else if(reader.hasBatch()) { } else if(reader.hasMessage()) {
UnverifiedBatch b = reader.readBatch(); UnverifiedMessage m = reader.readMessage();
verificationExecutor.execute(new VerifyBatch(b)); verificationExecutor.execute(new VerifyMessage(m));
} else if(reader.hasOffer()) { } else if(reader.hasOffer()) {
Offer o = reader.readOffer(); Offer o = reader.readOffer();
dbExecutor.execute(new ReceiveOffer(o)); dbExecutor.execute(new ReceiveOffer(o));
@@ -260,18 +263,18 @@ abstract class DuplexConnection implements DatabaseListener {
} }
// This task runs on a verification thread // This task runs on a verification thread
private class VerifyBatch implements Runnable { private class VerifyMessage implements Runnable {
private final UnverifiedBatch batch; private final UnverifiedMessage message;
private VerifyBatch(UnverifiedBatch batch) { private VerifyMessage(UnverifiedMessage message) {
this.batch = batch; this.message = message;
} }
public void run() { public void run() {
try { try {
Batch b = batch.verify(); Message m = messageVerifier.verifyMessage(message);
dbExecutor.execute(new ReceiveBatch(b)); dbExecutor.execute(new ReceiveMessage(m));
} catch(GeneralSecurityException e) { } catch(GeneralSecurityException e) {
if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
} }
@@ -279,17 +282,17 @@ abstract class DuplexConnection implements DatabaseListener {
} }
// This task runs on a database thread // This task runs on a database thread
private class ReceiveBatch implements Runnable { private class ReceiveMessage implements Runnable {
private final Batch batch; private final Message message;
private ReceiveBatch(Batch batch) { private ReceiveMessage(Message message) {
this.batch = batch; this.message = message;
} }
public void run() { public void run() {
try { try {
db.receiveBatch(contactId, batch); db.receiveMessage(contactId, message);
} catch(DbException e) { } catch(DbException e) {
if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
} }
@@ -394,9 +397,9 @@ abstract class DuplexConnection implements DatabaseListener {
public void run() { public void run() {
assert writer != null; assert writer != null;
int maxBatches = writer.getMaxBatchesForAck(Long.MAX_VALUE); int maxMessages = writer.getMaxMessagesForAck(Long.MAX_VALUE);
try { try {
Ack a = db.generateAck(contactId, maxBatches); Ack a = db.generateAck(contactId, maxMessages);
if(a != null) writerTasks.add(new WriteAck(a)); if(a != null) writerTasks.add(new WriteAck(a));
} catch(DbException e) { } catch(DbException e) {
if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
@@ -436,11 +439,11 @@ abstract class DuplexConnection implements DatabaseListener {
public void run() { public void run() {
assert writer != null; assert writer != null;
int capacity = writer.getMessageCapacityForBatch(Long.MAX_VALUE);
try { try {
RawBatch b = db.generateBatch(contactId, capacity, requested); Collection<byte[]> batch = db.generateBatch(contactId,
if(b == null) new GenerateOffer().run(); Integer.MAX_VALUE, requested);
else writerTasks.add(new WriteBatch(b, requested)); if(batch == null) new GenerateOffer().run();
else writerTasks.add(new WriteBatch(batch, requested));
} catch(DbException e) { } catch(DbException e) {
if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
} }
@@ -450,10 +453,11 @@ abstract class DuplexConnection implements DatabaseListener {
// This task runs on the writer thread // This task runs on the writer thread
private class WriteBatch implements Runnable { private class WriteBatch implements Runnable {
private final RawBatch batch; private final Collection<byte[]> batch;
private final Collection<MessageId> requested; private final Collection<MessageId> requested;
private WriteBatch(RawBatch batch, Collection<MessageId> requested) { private WriteBatch(Collection<byte[]> batch,
Collection<MessageId> requested) {
this.batch = batch; this.batch = batch;
this.requested = requested; this.requested = requested;
} }
@@ -461,7 +465,7 @@ abstract class DuplexConnection implements DatabaseListener {
public void run() { public void run() {
assert writer != null; assert writer != null;
try { try {
writer.writeBatch(batch); for(byte[] raw : batch) writer.writeMessage(raw);
if(requested.isEmpty()) dbExecutor.execute(new GenerateOffer()); if(requested.isEmpty()) dbExecutor.execute(new GenerateOffer());
else dbExecutor.execute(new GenerateBatches(requested)); else dbExecutor.execute(new GenerateBatches(requested));
} catch(IOException e) { } catch(IOException e) {

View File

@@ -10,6 +10,7 @@ import net.sf.briar.api.crypto.KeyManager;
import net.sf.briar.api.db.DatabaseComponent; import net.sf.briar.api.db.DatabaseComponent;
import net.sf.briar.api.db.DatabaseExecutor; import net.sf.briar.api.db.DatabaseExecutor;
import net.sf.briar.api.plugins.duplex.DuplexTransportConnection; import net.sf.briar.api.plugins.duplex.DuplexTransportConnection;
import net.sf.briar.api.protocol.MessageVerifier;
import net.sf.briar.api.protocol.ProtocolReaderFactory; import net.sf.briar.api.protocol.ProtocolReaderFactory;
import net.sf.briar.api.protocol.ProtocolWriterFactory; import net.sf.briar.api.protocol.ProtocolWriterFactory;
import net.sf.briar.api.protocol.TransportId; import net.sf.briar.api.protocol.TransportId;
@@ -28,6 +29,7 @@ class DuplexConnectionFactoryImpl implements DuplexConnectionFactory {
Logger.getLogger(DuplexConnectionFactoryImpl.class.getName()); Logger.getLogger(DuplexConnectionFactoryImpl.class.getName());
private final Executor dbExecutor, verificationExecutor; private final Executor dbExecutor, verificationExecutor;
private final MessageVerifier messageVerifier;
private final DatabaseComponent db; private final DatabaseComponent db;
private final KeyManager keyManager; private final KeyManager keyManager;
private final ConnectionRegistry connRegistry; private final ConnectionRegistry connRegistry;
@@ -39,13 +41,14 @@ class DuplexConnectionFactoryImpl implements DuplexConnectionFactory {
@Inject @Inject
DuplexConnectionFactoryImpl(@DatabaseExecutor Executor dbExecutor, DuplexConnectionFactoryImpl(@DatabaseExecutor Executor dbExecutor,
@VerificationExecutor Executor verificationExecutor, @VerificationExecutor Executor verificationExecutor,
DatabaseComponent db, KeyManager keyManager, MessageVerifier messageVerifier, DatabaseComponent db,
ConnectionRegistry connRegistry, KeyManager keyManager, ConnectionRegistry connRegistry,
ConnectionReaderFactory connReaderFactory, ConnectionReaderFactory connReaderFactory,
ConnectionWriterFactory connWriterFactory, ConnectionWriterFactory connWriterFactory,
ProtocolReaderFactory protoReaderFactory, ProtocolWriterFactory protoWriterFactory) { ProtocolReaderFactory protoReaderFactory, ProtocolWriterFactory protoWriterFactory) {
this.dbExecutor = dbExecutor; this.dbExecutor = dbExecutor;
this.verificationExecutor = verificationExecutor; this.verificationExecutor = verificationExecutor;
this.messageVerifier = messageVerifier;
this.db = db; this.db = db;
this.keyManager = keyManager; this.keyManager = keyManager;
this.connRegistry = connRegistry; this.connRegistry = connRegistry;
@@ -58,9 +61,9 @@ class DuplexConnectionFactoryImpl implements DuplexConnectionFactory {
public void createIncomingConnection(ConnectionContext ctx, public void createIncomingConnection(ConnectionContext ctx,
DuplexTransportConnection transport) { DuplexTransportConnection transport) {
final DuplexConnection conn = new IncomingDuplexConnection(dbExecutor, final DuplexConnection conn = new IncomingDuplexConnection(dbExecutor,
verificationExecutor, db, connRegistry, connReaderFactory, verificationExecutor, messageVerifier, db, connRegistry,
connWriterFactory, protoReaderFactory, protoWriterFactory, ctx, connReaderFactory, connWriterFactory, protoReaderFactory,
transport); protoWriterFactory, ctx, transport);
Runnable write = new Runnable() { Runnable write = new Runnable() {
public void run() { public void run() {
conn.write(); conn.write();
@@ -84,9 +87,9 @@ class DuplexConnectionFactoryImpl implements DuplexConnectionFactory {
return; return;
} }
final DuplexConnection conn = new OutgoingDuplexConnection(dbExecutor, final DuplexConnection conn = new OutgoingDuplexConnection(dbExecutor,
verificationExecutor, db, connRegistry, connReaderFactory, verificationExecutor, messageVerifier, db, connRegistry,
connWriterFactory, protoReaderFactory, protoWriterFactory, ctx, connReaderFactory, connWriterFactory, protoReaderFactory,
transport); protoWriterFactory, ctx, transport);
Runnable write = new Runnable() { Runnable write = new Runnable() {
public void run() { public void run() {
conn.write(); conn.write();

View File

@@ -6,6 +6,7 @@ import java.util.concurrent.Executor;
import net.sf.briar.api.db.DatabaseComponent; import net.sf.briar.api.db.DatabaseComponent;
import net.sf.briar.api.db.DatabaseExecutor; import net.sf.briar.api.db.DatabaseExecutor;
import net.sf.briar.api.plugins.duplex.DuplexTransportConnection; import net.sf.briar.api.plugins.duplex.DuplexTransportConnection;
import net.sf.briar.api.protocol.MessageVerifier;
import net.sf.briar.api.protocol.ProtocolReaderFactory; import net.sf.briar.api.protocol.ProtocolReaderFactory;
import net.sf.briar.api.protocol.ProtocolWriterFactory; import net.sf.briar.api.protocol.ProtocolWriterFactory;
import net.sf.briar.api.protocol.VerificationExecutor; import net.sf.briar.api.protocol.VerificationExecutor;
@@ -20,15 +21,16 @@ class IncomingDuplexConnection extends DuplexConnection {
IncomingDuplexConnection(@DatabaseExecutor Executor dbExecutor, IncomingDuplexConnection(@DatabaseExecutor Executor dbExecutor,
@VerificationExecutor Executor verificationExecutor, @VerificationExecutor Executor verificationExecutor,
DatabaseComponent db, ConnectionRegistry connRegistry, MessageVerifier messageVerifier, DatabaseComponent db,
ConnectionRegistry connRegistry,
ConnectionReaderFactory connReaderFactory, ConnectionReaderFactory connReaderFactory,
ConnectionWriterFactory connWriterFactory, ConnectionWriterFactory connWriterFactory,
ProtocolReaderFactory protoReaderFactory, ProtocolReaderFactory protoReaderFactory,
ProtocolWriterFactory protoWriterFactory, ProtocolWriterFactory protoWriterFactory,
ConnectionContext ctx, DuplexTransportConnection transport) { ConnectionContext ctx, DuplexTransportConnection transport) {
super(dbExecutor, verificationExecutor, db, connRegistry, super(dbExecutor, verificationExecutor, messageVerifier, db,
connReaderFactory, connWriterFactory, protoReaderFactory, connRegistry, connReaderFactory, connWriterFactory,
protoWriterFactory, ctx, transport); protoReaderFactory, protoWriterFactory, ctx, transport);
} }
@Override @Override

View File

@@ -6,6 +6,7 @@ import java.util.concurrent.Executor;
import net.sf.briar.api.db.DatabaseComponent; import net.sf.briar.api.db.DatabaseComponent;
import net.sf.briar.api.db.DatabaseExecutor; import net.sf.briar.api.db.DatabaseExecutor;
import net.sf.briar.api.plugins.duplex.DuplexTransportConnection; import net.sf.briar.api.plugins.duplex.DuplexTransportConnection;
import net.sf.briar.api.protocol.MessageVerifier;
import net.sf.briar.api.protocol.ProtocolReaderFactory; import net.sf.briar.api.protocol.ProtocolReaderFactory;
import net.sf.briar.api.protocol.ProtocolWriterFactory; import net.sf.briar.api.protocol.ProtocolWriterFactory;
import net.sf.briar.api.protocol.VerificationExecutor; import net.sf.briar.api.protocol.VerificationExecutor;
@@ -20,15 +21,16 @@ class OutgoingDuplexConnection extends DuplexConnection {
OutgoingDuplexConnection(@DatabaseExecutor Executor dbExecutor, OutgoingDuplexConnection(@DatabaseExecutor Executor dbExecutor,
@VerificationExecutor Executor verificationExecutor, @VerificationExecutor Executor verificationExecutor,
DatabaseComponent db, ConnectionRegistry connRegistry, MessageVerifier messageVerifier, DatabaseComponent db,
ConnectionRegistry connRegistry,
ConnectionReaderFactory connReaderFactory, ConnectionReaderFactory connReaderFactory,
ConnectionWriterFactory connWriterFactory, ConnectionWriterFactory connWriterFactory,
ProtocolReaderFactory protoReaderFactory, ProtocolReaderFactory protoReaderFactory,
ProtocolWriterFactory protoWriterFactory, ConnectionContext ctx, ProtocolWriterFactory protoWriterFactory, ConnectionContext ctx,
DuplexTransportConnection transport) { DuplexTransportConnection transport) {
super(dbExecutor, verificationExecutor, db, connRegistry, super(dbExecutor, verificationExecutor, messageVerifier, db,
connReaderFactory, connWriterFactory, protoReaderFactory, connRegistry, connReaderFactory, connWriterFactory,
protoWriterFactory, ctx, transport); protoReaderFactory, protoWriterFactory, ctx, transport);
} }
@Override @Override

View File

@@ -15,13 +15,14 @@ import net.sf.briar.api.db.DatabaseExecutor;
import net.sf.briar.api.db.DbException; import net.sf.briar.api.db.DbException;
import net.sf.briar.api.plugins.simplex.SimplexTransportReader; import net.sf.briar.api.plugins.simplex.SimplexTransportReader;
import net.sf.briar.api.protocol.Ack; import net.sf.briar.api.protocol.Ack;
import net.sf.briar.api.protocol.Batch; import net.sf.briar.api.protocol.Message;
import net.sf.briar.api.protocol.MessageVerifier;
import net.sf.briar.api.protocol.ProtocolReader; import net.sf.briar.api.protocol.ProtocolReader;
import net.sf.briar.api.protocol.ProtocolReaderFactory; import net.sf.briar.api.protocol.ProtocolReaderFactory;
import net.sf.briar.api.protocol.SubscriptionUpdate; import net.sf.briar.api.protocol.SubscriptionUpdate;
import net.sf.briar.api.protocol.TransportId; import net.sf.briar.api.protocol.TransportId;
import net.sf.briar.api.protocol.TransportUpdate; import net.sf.briar.api.protocol.TransportUpdate;
import net.sf.briar.api.protocol.UnverifiedBatch; import net.sf.briar.api.protocol.UnverifiedMessage;
import net.sf.briar.api.protocol.VerificationExecutor; import net.sf.briar.api.protocol.VerificationExecutor;
import net.sf.briar.api.transport.ConnectionContext; import net.sf.briar.api.transport.ConnectionContext;
import net.sf.briar.api.transport.ConnectionReader; import net.sf.briar.api.transport.ConnectionReader;
@@ -35,6 +36,7 @@ class IncomingSimplexConnection {
Logger.getLogger(IncomingSimplexConnection.class.getName()); Logger.getLogger(IncomingSimplexConnection.class.getName());
private final Executor dbExecutor, verificationExecutor; private final Executor dbExecutor, verificationExecutor;
private final MessageVerifier messageVerifier;
private final DatabaseComponent db; private final DatabaseComponent db;
private final ConnectionRegistry connRegistry; private final ConnectionRegistry connRegistry;
private final ConnectionReaderFactory connFactory; private final ConnectionReaderFactory connFactory;
@@ -46,12 +48,14 @@ class IncomingSimplexConnection {
IncomingSimplexConnection(@DatabaseExecutor Executor dbExecutor, IncomingSimplexConnection(@DatabaseExecutor Executor dbExecutor,
@VerificationExecutor Executor verificationExecutor, @VerificationExecutor Executor verificationExecutor,
DatabaseComponent db, ConnectionRegistry connRegistry, MessageVerifier messageVerifier, DatabaseComponent db,
ConnectionRegistry connRegistry,
ConnectionReaderFactory connFactory, ConnectionReaderFactory connFactory,
ProtocolReaderFactory protoFactory, ConnectionContext ctx, ProtocolReaderFactory protoFactory, ConnectionContext ctx,
SimplexTransportReader transport) { SimplexTransportReader transport) {
this.dbExecutor = dbExecutor; this.dbExecutor = dbExecutor;
this.verificationExecutor = verificationExecutor; this.verificationExecutor = verificationExecutor;
this.messageVerifier = messageVerifier;
this.db = db; this.db = db;
this.connRegistry = connRegistry; this.connRegistry = connRegistry;
this.connFactory = connFactory; this.connFactory = connFactory;
@@ -74,9 +78,9 @@ class IncomingSimplexConnection {
if(reader.hasAck()) { if(reader.hasAck()) {
Ack a = reader.readAck(); Ack a = reader.readAck();
dbExecutor.execute(new ReceiveAck(a)); dbExecutor.execute(new ReceiveAck(a));
} else if(reader.hasBatch()) { } else if(reader.hasMessage()) {
UnverifiedBatch b = reader.readBatch(); UnverifiedMessage m = reader.readMessage();
verificationExecutor.execute(new VerifyBatch(b)); verificationExecutor.execute(new VerifyMessage(m));
} else if(reader.hasSubscriptionUpdate()) { } else if(reader.hasSubscriptionUpdate()) {
SubscriptionUpdate s = reader.readSubscriptionUpdate(); SubscriptionUpdate s = reader.readSubscriptionUpdate();
dbExecutor.execute(new ReceiveSubscriptionUpdate(s)); dbExecutor.execute(new ReceiveSubscriptionUpdate(s));
@@ -122,35 +126,35 @@ class IncomingSimplexConnection {
} }
} }
private class VerifyBatch implements Runnable { private class VerifyMessage implements Runnable {
private final UnverifiedBatch batch; private final UnverifiedMessage message;
private VerifyBatch(UnverifiedBatch batch) { private VerifyMessage(UnverifiedMessage message) {
this.batch = batch; this.message = message;
} }
public void run() { public void run() {
try { try {
Batch b = batch.verify(); Message m = messageVerifier.verifyMessage(message);
dbExecutor.execute(new ReceiveBatch(b)); dbExecutor.execute(new ReceiveMessage(m));
} catch(GeneralSecurityException e) { } catch(GeneralSecurityException e) {
if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
} }
} }
} }
private class ReceiveBatch implements Runnable { private class ReceiveMessage implements Runnable {
private final Batch batch; private final Message message;
private ReceiveBatch(Batch batch) { private ReceiveMessage(Message message) {
this.batch = batch; this.message = message;
} }
public void run() { public void run() {
try { try {
db.receiveBatch(contactId, batch); db.receiveMessage(contactId, message);
} catch(DbException e) { } catch(DbException e) {
if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
} }

View File

@@ -6,6 +6,7 @@ import static net.sf.briar.api.protocol.ProtocolConstants.MAX_PACKET_LENGTH;
import java.io.EOFException; import java.io.EOFException;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.Collection;
import java.util.logging.Logger; import java.util.logging.Logger;
import net.sf.briar.api.ContactId; import net.sf.briar.api.ContactId;
@@ -15,7 +16,6 @@ import net.sf.briar.api.plugins.simplex.SimplexTransportWriter;
import net.sf.briar.api.protocol.Ack; import net.sf.briar.api.protocol.Ack;
import net.sf.briar.api.protocol.ProtocolWriter; import net.sf.briar.api.protocol.ProtocolWriter;
import net.sf.briar.api.protocol.ProtocolWriterFactory; import net.sf.briar.api.protocol.ProtocolWriterFactory;
import net.sf.briar.api.protocol.RawBatch;
import net.sf.briar.api.protocol.SubscriptionUpdate; import net.sf.briar.api.protocol.SubscriptionUpdate;
import net.sf.briar.api.protocol.TransportId; import net.sf.briar.api.protocol.TransportId;
import net.sf.briar.api.protocol.TransportUpdate; import net.sf.briar.api.protocol.TransportUpdate;
@@ -77,23 +77,23 @@ class OutgoingSimplexConnection {
} }
// Write acks until you can't write acks no more // Write acks until you can't write acks no more
capacity = conn.getRemainingCapacity(); capacity = conn.getRemainingCapacity();
int maxBatches = writer.getMaxBatchesForAck(capacity); int maxMessages = writer.getMaxMessagesForAck(capacity);
Ack a = db.generateAck(contactId, maxBatches); Ack a = db.generateAck(contactId, maxMessages);
while(a != null) { while(a != null) {
writer.writeAck(a); writer.writeAck(a);
capacity = conn.getRemainingCapacity(); capacity = conn.getRemainingCapacity();
maxBatches = writer.getMaxBatchesForAck(capacity); maxMessages = writer.getMaxMessagesForAck(capacity);
a = db.generateAck(contactId, maxBatches); a = db.generateAck(contactId, maxMessages);
} }
// Write batches until you can't write batches no more // Write messages until you can't write messages no more
capacity = conn.getRemainingCapacity(); capacity = conn.getRemainingCapacity();
capacity = writer.getMessageCapacityForBatch(capacity); int maxLength = (int) Math.min(capacity, MAX_PACKET_LENGTH);
RawBatch b = db.generateBatch(contactId, (int) capacity); Collection<byte[]> batch = db.generateBatch(contactId, maxLength);
while(b != null) { while(batch != null) {
writer.writeBatch(b); for(byte[] raw : batch) writer.writeMessage(raw);
capacity = conn.getRemainingCapacity(); capacity = conn.getRemainingCapacity();
capacity = writer.getMessageCapacityForBatch(capacity); maxLength = (int) Math.min(capacity, MAX_PACKET_LENGTH);
b = db.generateBatch(contactId, (int) capacity); batch = db.generateBatch(contactId, maxLength);
} }
writer.flush(); writer.flush();
writer.close(); writer.close();

View File

@@ -11,6 +11,7 @@ import net.sf.briar.api.db.DatabaseComponent;
import net.sf.briar.api.db.DatabaseExecutor; import net.sf.briar.api.db.DatabaseExecutor;
import net.sf.briar.api.plugins.simplex.SimplexTransportReader; import net.sf.briar.api.plugins.simplex.SimplexTransportReader;
import net.sf.briar.api.plugins.simplex.SimplexTransportWriter; import net.sf.briar.api.plugins.simplex.SimplexTransportWriter;
import net.sf.briar.api.protocol.MessageVerifier;
import net.sf.briar.api.protocol.ProtocolReaderFactory; import net.sf.briar.api.protocol.ProtocolReaderFactory;
import net.sf.briar.api.protocol.ProtocolWriterFactory; import net.sf.briar.api.protocol.ProtocolWriterFactory;
import net.sf.briar.api.protocol.TransportId; import net.sf.briar.api.protocol.TransportId;
@@ -29,6 +30,7 @@ class SimplexConnectionFactoryImpl implements SimplexConnectionFactory {
Logger.getLogger(SimplexConnectionFactoryImpl.class.getName()); Logger.getLogger(SimplexConnectionFactoryImpl.class.getName());
private final Executor dbExecutor, verificationExecutor; private final Executor dbExecutor, verificationExecutor;
private final MessageVerifier messageVerifier;
private final DatabaseComponent db; private final DatabaseComponent db;
private final KeyManager keyManager; private final KeyManager keyManager;
private final ConnectionRegistry connRegistry; private final ConnectionRegistry connRegistry;
@@ -40,14 +42,15 @@ class SimplexConnectionFactoryImpl implements SimplexConnectionFactory {
@Inject @Inject
SimplexConnectionFactoryImpl(@DatabaseExecutor Executor dbExecutor, SimplexConnectionFactoryImpl(@DatabaseExecutor Executor dbExecutor,
@VerificationExecutor Executor verificationExecutor, @VerificationExecutor Executor verificationExecutor,
DatabaseComponent db, KeyManager keyManager, MessageVerifier messageVerifier, DatabaseComponent db,
ConnectionRegistry connRegistry, KeyManager keyManager, ConnectionRegistry connRegistry,
ConnectionReaderFactory connReaderFactory, ConnectionReaderFactory connReaderFactory,
ConnectionWriterFactory connWriterFactory, ConnectionWriterFactory connWriterFactory,
ProtocolReaderFactory protoReaderFactory, ProtocolReaderFactory protoReaderFactory,
ProtocolWriterFactory protoWriterFactory) { ProtocolWriterFactory protoWriterFactory) {
this.dbExecutor = dbExecutor; this.dbExecutor = dbExecutor;
this.verificationExecutor = verificationExecutor; this.verificationExecutor = verificationExecutor;
this.messageVerifier = messageVerifier;
this.db = db; this.db = db;
this.keyManager = keyManager; this.keyManager = keyManager;
this.connRegistry = connRegistry; this.connRegistry = connRegistry;
@@ -59,8 +62,8 @@ class SimplexConnectionFactoryImpl implements SimplexConnectionFactory {
public void createIncomingConnection(ConnectionContext ctx, SimplexTransportReader r) { public void createIncomingConnection(ConnectionContext ctx, SimplexTransportReader r) {
final IncomingSimplexConnection conn = new IncomingSimplexConnection( final IncomingSimplexConnection conn = new IncomingSimplexConnection(
dbExecutor, verificationExecutor, db, connRegistry, dbExecutor, verificationExecutor, messageVerifier, db,
connReaderFactory, protoReaderFactory, ctx, r); connRegistry, connReaderFactory, protoReaderFactory, ctx, r);
Runnable read = new Runnable() { Runnable read = new Runnable() {
public void run() { public void run() {
conn.read(); conn.read();

View File

@@ -89,14 +89,11 @@
<test name='net.sf.briar.plugins.modem.ModemPluginTest'/> <test name='net.sf.briar.plugins.modem.ModemPluginTest'/>
<test name='net.sf.briar.plugins.tcp.LanTcpPluginTest'/> <test name='net.sf.briar.plugins.tcp.LanTcpPluginTest'/>
<test name='net.sf.briar.protocol.AckReaderTest'/> <test name='net.sf.briar.protocol.AckReaderTest'/>
<test name='net.sf.briar.protocol.BatchReaderTest'/>
<test name='net.sf.briar.protocol.ConstantsTest'/> <test name='net.sf.briar.protocol.ConstantsTest'/>
<test name='net.sf.briar.protocol.ConsumersTest'/> <test name='net.sf.briar.protocol.ConsumersTest'/>
<test name='net.sf.briar.protocol.OfferReaderTest'/> <test name='net.sf.briar.protocol.OfferReaderTest'/>
<test name='net.sf.briar.protocol.ProtocolIntegrationTest'/>
<test name='net.sf.briar.protocol.ProtocolWriterImplTest'/> <test name='net.sf.briar.protocol.ProtocolWriterImplTest'/>
<test name='net.sf.briar.protocol.RequestReaderTest'/> <test name='net.sf.briar.protocol.RequestReaderTest'/>
<test name='net.sf.briar.protocol.UnverifiedBatchImplTest'/>
<test name='net.sf.briar.protocol.simplex.OutgoingSimplexConnectionTest'/> <test name='net.sf.briar.protocol.simplex.OutgoingSimplexConnectionTest'/>
<test name='net.sf.briar.protocol.simplex.SimplexProtocolIntegrationTest'/> <test name='net.sf.briar.protocol.simplex.SimplexProtocolIntegrationTest'/>
<test name='net.sf.briar.serial.ReaderImplTest'/> <test name='net.sf.briar.serial.ReaderImplTest'/>

View File

@@ -12,7 +12,6 @@ import java.util.Arrays;
import java.util.BitSet; import java.util.BitSet;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import java.util.Random; import java.util.Random;
@@ -22,26 +21,25 @@ import net.sf.briar.api.crypto.CryptoComponent;
import net.sf.briar.api.protocol.Ack; import net.sf.briar.api.protocol.Ack;
import net.sf.briar.api.protocol.Author; import net.sf.briar.api.protocol.Author;
import net.sf.briar.api.protocol.AuthorFactory; import net.sf.briar.api.protocol.AuthorFactory;
import net.sf.briar.api.protocol.Batch;
import net.sf.briar.api.protocol.BatchId;
import net.sf.briar.api.protocol.Group; import net.sf.briar.api.protocol.Group;
import net.sf.briar.api.protocol.GroupFactory; import net.sf.briar.api.protocol.GroupFactory;
import net.sf.briar.api.protocol.GroupId; import net.sf.briar.api.protocol.GroupId;
import net.sf.briar.api.protocol.Message; import net.sf.briar.api.protocol.Message;
import net.sf.briar.api.protocol.MessageFactory; import net.sf.briar.api.protocol.MessageFactory;
import net.sf.briar.api.protocol.MessageId; import net.sf.briar.api.protocol.MessageId;
import net.sf.briar.api.protocol.MessageVerifier;
import net.sf.briar.api.protocol.Offer; import net.sf.briar.api.protocol.Offer;
import net.sf.briar.api.protocol.PacketFactory; import net.sf.briar.api.protocol.PacketFactory;
import net.sf.briar.api.protocol.ProtocolReader; import net.sf.briar.api.protocol.ProtocolReader;
import net.sf.briar.api.protocol.ProtocolReaderFactory; import net.sf.briar.api.protocol.ProtocolReaderFactory;
import net.sf.briar.api.protocol.ProtocolWriter; import net.sf.briar.api.protocol.ProtocolWriter;
import net.sf.briar.api.protocol.ProtocolWriterFactory; import net.sf.briar.api.protocol.ProtocolWriterFactory;
import net.sf.briar.api.protocol.RawBatch;
import net.sf.briar.api.protocol.Request; import net.sf.briar.api.protocol.Request;
import net.sf.briar.api.protocol.SubscriptionUpdate; import net.sf.briar.api.protocol.SubscriptionUpdate;
import net.sf.briar.api.protocol.Transport; import net.sf.briar.api.protocol.Transport;
import net.sf.briar.api.protocol.TransportId; import net.sf.briar.api.protocol.TransportId;
import net.sf.briar.api.protocol.TransportUpdate; import net.sf.briar.api.protocol.TransportUpdate;
import net.sf.briar.api.protocol.UnverifiedMessage;
import net.sf.briar.api.transport.ConnectionContext; import net.sf.briar.api.transport.ConnectionContext;
import net.sf.briar.api.transport.ConnectionReader; import net.sf.briar.api.transport.ConnectionReader;
import net.sf.briar.api.transport.ConnectionReaderFactory; import net.sf.briar.api.transport.ConnectionReaderFactory;
@@ -64,15 +62,13 @@ import com.google.inject.Injector;
public class ProtocolIntegrationTest extends BriarTestCase { public class ProtocolIntegrationTest extends BriarTestCase {
private final BatchId ack = new BatchId(TestUtils.getRandomId());
private final long timestamp = System.currentTimeMillis();
private final ConnectionReaderFactory connectionReaderFactory; private final ConnectionReaderFactory connectionReaderFactory;
private final ConnectionWriterFactory connectionWriterFactory; private final ConnectionWriterFactory connectionWriterFactory;
private final ProtocolReaderFactory protocolReaderFactory; private final ProtocolReaderFactory protocolReaderFactory;
private final ProtocolWriterFactory protocolWriterFactory; private final ProtocolWriterFactory protocolWriterFactory;
private final PacketFactory packetFactory; private final PacketFactory packetFactory;
private final CryptoComponent crypto; private final MessageVerifier messageVerifier;
private final ContactId contactId; private final ContactId contactId;
private final TransportId transportId; private final TransportId transportId;
private final byte[] secret; private final byte[] secret;
@@ -82,30 +78,32 @@ public class ProtocolIntegrationTest extends BriarTestCase {
private final String authorName = "Alice"; private final String authorName = "Alice";
private final String subject = "Hello"; private final String subject = "Hello";
private final String messageBody = "Hello world"; private final String messageBody = "Hello world";
private final Collection<MessageId> messageIds;
private final Collection<Transport> transports; private final Collection<Transport> transports;
private final long timestamp = System.currentTimeMillis();
public ProtocolIntegrationTest() throws Exception { public ProtocolIntegrationTest() throws Exception {
super(); super();
Injector i = Guice.createInjector(new ClockModule(), new CryptoModule(), Injector i = Guice.createInjector(new TestDatabaseModule(),
new DatabaseModule(), new LifecycleModule(), new ClockModule(), new CryptoModule(), new DatabaseModule(),
new ProtocolModule(), new SerialModule(), new LifecycleModule(), new ProtocolModule(),
new TestDatabaseModule(), new SimplexProtocolModule(), new DuplexProtocolModule(), new SimplexProtocolModule(),
new TransportModule(), new DuplexProtocolModule()); new SerialModule(), new TransportModule());
connectionReaderFactory = i.getInstance(ConnectionReaderFactory.class); connectionReaderFactory = i.getInstance(ConnectionReaderFactory.class);
connectionWriterFactory = i.getInstance(ConnectionWriterFactory.class); connectionWriterFactory = i.getInstance(ConnectionWriterFactory.class);
protocolReaderFactory = i.getInstance(ProtocolReaderFactory.class); protocolReaderFactory = i.getInstance(ProtocolReaderFactory.class);
protocolWriterFactory = i.getInstance(ProtocolWriterFactory.class); protocolWriterFactory = i.getInstance(ProtocolWriterFactory.class);
packetFactory = i.getInstance(PacketFactory.class); packetFactory = i.getInstance(PacketFactory.class);
crypto = i.getInstance(CryptoComponent.class); messageVerifier = i.getInstance(MessageVerifier.class);
contactId = new ContactId(234); contactId = new ContactId(234);
transportId = new TransportId(TestUtils.getRandomId()); transportId = new TransportId(TestUtils.getRandomId());
// Create a shared secret // Create a shared secret
Random r = new Random();
secret = new byte[32]; secret = new byte[32];
r.nextBytes(secret); new Random().nextBytes(secret);
// Create two groups: one restricted, one unrestricted // Create two groups: one restricted, one unrestricted
GroupFactory groupFactory = i.getInstance(GroupFactory.class); GroupFactory groupFactory = i.getInstance(GroupFactory.class);
group = groupFactory.createGroup("Unrestricted group", null); group = groupFactory.createGroup("Unrestricted group", null);
CryptoComponent crypto = i.getInstance(CryptoComponent.class);
KeyPair groupKeyPair = crypto.generateSignatureKeyPair(); KeyPair groupKeyPair = crypto.generateSignatureKeyPair();
group1 = groupFactory.createGroup("Restricted group", group1 = groupFactory.createGroup("Restricted group",
groupKeyPair.getPublic().getEncoded()); groupKeyPair.getPublic().getEncoded());
@@ -127,6 +125,8 @@ public class ProtocolIntegrationTest extends BriarTestCase {
message3 = messageFactory.createMessage(null, group1, message3 = messageFactory.createMessage(null, group1,
groupKeyPair.getPrivate(), author, authorKeyPair.getPrivate(), groupKeyPair.getPrivate(), author, authorKeyPair.getPrivate(),
subject, messageBody.getBytes("UTF-8")); subject, messageBody.getBytes("UTF-8"));
messageIds = Arrays.asList(message.getId(),
message1.getId(), message2.getId(), message3.getId());
// Create some transports // Create some transports
TransportId transportId = new TransportId(TestUtils.getRandomId()); TransportId transportId = new TransportId(TestUtils.getRandomId());
Transport transport = new Transport(transportId, Transport transport = new Transport(transportId,
@@ -149,18 +149,15 @@ public class ProtocolIntegrationTest extends BriarTestCase {
ProtocolWriter writer = protocolWriterFactory.createProtocolWriter(out1, ProtocolWriter writer = protocolWriterFactory.createProtocolWriter(out1,
false); false);
Ack a = packetFactory.createAck(Collections.singletonList(ack)); Ack a = packetFactory.createAck(messageIds);
writer.writeAck(a); writer.writeAck(a);
Collection<byte[]> batch = Arrays.asList(message.getSerialised(), writer.writeMessage(message.getSerialised());
message1.getSerialised(), message2.getSerialised(), writer.writeMessage(message1.getSerialised());
message3.getSerialised()); writer.writeMessage(message2.getSerialised());
RawBatch b = packetFactory.createBatch(batch); writer.writeMessage(message3.getSerialised());
writer.writeBatch(b);
Collection<MessageId> offer = Arrays.asList(message.getId(), Offer o = packetFactory.createOffer(messageIds);
message1.getId(), message2.getId(), message3.getId());
Offer o = packetFactory.createOffer(offer);
writer.writeOffer(o); writer.writeOffer(o);
BitSet requested = new BitSet(4); BitSet requested = new BitSet(4);
@@ -200,29 +197,26 @@ public class ProtocolIntegrationTest extends BriarTestCase {
// Read the ack // Read the ack
assertTrue(reader.hasAck()); assertTrue(reader.hasAck());
Ack a = reader.readAck(); Ack a = reader.readAck();
assertEquals(Collections.singletonList(ack), a.getBatchIds()); assertEquals(messageIds, a.getMessageIds());
// Read and verify the batch // Read and verify the messages
assertTrue(reader.hasBatch()); assertTrue(reader.hasMessage());
Batch b = reader.readBatch().verify(); UnverifiedMessage m = reader.readMessage();
Collection<Message> messages = b.getMessages(); checkMessageEquality(message, messageVerifier.verifyMessage(m));
assertEquals(4, messages.size()); assertTrue(reader.hasMessage());
Iterator<Message> it = messages.iterator(); m = reader.readMessage();
checkMessageEquality(message, it.next()); checkMessageEquality(message1, messageVerifier.verifyMessage(m));
checkMessageEquality(message1, it.next()); assertTrue(reader.hasMessage());
checkMessageEquality(message2, it.next()); m = reader.readMessage();
checkMessageEquality(message3, it.next()); checkMessageEquality(message2, messageVerifier.verifyMessage(m));
assertTrue(reader.hasMessage());
m = reader.readMessage();
checkMessageEquality(message3, messageVerifier.verifyMessage(m));
// Read the offer // Read the offer
assertTrue(reader.hasOffer()); assertTrue(reader.hasOffer());
Offer o = reader.readOffer(); Offer o = reader.readOffer();
Collection<MessageId> offered = o.getMessageIds(); assertEquals(messageIds, o.getMessageIds());
assertEquals(4, offered.size());
Iterator<MessageId> it1 = offered.iterator();
assertEquals(message.getId(), it1.next());
assertEquals(message1.getId(), it1.next());
assertEquals(message2.getId(), it1.next());
assertEquals(message3.getId(), it1.next());
// Read the request // Read the request
assertTrue(reader.hasRequest()); assertTrue(reader.hasRequest());

View File

@@ -1,4 +1,4 @@
package net.sf.briar.db; package net.sf.briar;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.InputStream; import java.io.InputStream;
@@ -8,7 +8,7 @@ import net.sf.briar.api.protocol.GroupId;
import net.sf.briar.api.protocol.Message; import net.sf.briar.api.protocol.Message;
import net.sf.briar.api.protocol.MessageId; import net.sf.briar.api.protocol.MessageId;
class TestMessage implements Message { public class TestMessage implements Message {
private final MessageId id, parent; private final MessageId id, parent;
private final GroupId group; private final GroupId group;

View File

@@ -7,6 +7,7 @@ import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import net.sf.briar.BriarTestCase; import net.sf.briar.BriarTestCase;
import net.sf.briar.TestMessage;
import net.sf.briar.TestUtils; import net.sf.briar.TestUtils;
import net.sf.briar.api.ContactId; import net.sf.briar.api.ContactId;
import net.sf.briar.api.Rating; import net.sf.briar.api.Rating;
@@ -17,21 +18,18 @@ import net.sf.briar.api.db.NoSuchContactTransportException;
import net.sf.briar.api.db.event.ContactAddedEvent; import net.sf.briar.api.db.event.ContactAddedEvent;
import net.sf.briar.api.db.event.ContactRemovedEvent; import net.sf.briar.api.db.event.ContactRemovedEvent;
import net.sf.briar.api.db.event.DatabaseListener; import net.sf.briar.api.db.event.DatabaseListener;
import net.sf.briar.api.db.event.MessagesAddedEvent; import net.sf.briar.api.db.event.MessageAddedEvent;
import net.sf.briar.api.db.event.RatingChangedEvent; import net.sf.briar.api.db.event.RatingChangedEvent;
import net.sf.briar.api.db.event.SubscriptionsUpdatedEvent; import net.sf.briar.api.db.event.SubscriptionsUpdatedEvent;
import net.sf.briar.api.lifecycle.ShutdownManager; import net.sf.briar.api.lifecycle.ShutdownManager;
import net.sf.briar.api.protocol.Ack; import net.sf.briar.api.protocol.Ack;
import net.sf.briar.api.protocol.AuthorId; import net.sf.briar.api.protocol.AuthorId;
import net.sf.briar.api.protocol.Batch;
import net.sf.briar.api.protocol.BatchId;
import net.sf.briar.api.protocol.Group; import net.sf.briar.api.protocol.Group;
import net.sf.briar.api.protocol.GroupId; import net.sf.briar.api.protocol.GroupId;
import net.sf.briar.api.protocol.Message; import net.sf.briar.api.protocol.Message;
import net.sf.briar.api.protocol.MessageId; import net.sf.briar.api.protocol.MessageId;
import net.sf.briar.api.protocol.Offer; import net.sf.briar.api.protocol.Offer;
import net.sf.briar.api.protocol.PacketFactory; import net.sf.briar.api.protocol.PacketFactory;
import net.sf.briar.api.protocol.RawBatch;
import net.sf.briar.api.protocol.Request; import net.sf.briar.api.protocol.Request;
import net.sf.briar.api.protocol.SubscriptionUpdate; import net.sf.briar.api.protocol.SubscriptionUpdate;
import net.sf.briar.api.protocol.Transport; import net.sf.briar.api.protocol.Transport;
@@ -48,10 +46,9 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
protected final Object txn = new Object(); protected final Object txn = new Object();
protected final AuthorId authorId; protected final AuthorId authorId;
protected final BatchId batchId;
protected final ContactId contactId; protected final ContactId contactId;
protected final GroupId groupId; protected final GroupId groupId;
protected final MessageId messageId, parentId; protected final MessageId messageId, messageId1;
private final String subject; private final String subject;
private final long timestamp; private final long timestamp;
private final int size; private final int size;
@@ -66,11 +63,10 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
public DatabaseComponentTest() { public DatabaseComponentTest() {
super(); super();
authorId = new AuthorId(TestUtils.getRandomId()); authorId = new AuthorId(TestUtils.getRandomId());
batchId = new BatchId(TestUtils.getRandomId());
contactId = new ContactId(234); contactId = new ContactId(234);
groupId = new GroupId(TestUtils.getRandomId()); groupId = new GroupId(TestUtils.getRandomId());
messageId = new MessageId(TestUtils.getRandomId()); messageId = new MessageId(TestUtils.getRandomId());
parentId = new MessageId(TestUtils.getRandomId()); messageId1 = new MessageId(TestUtils.getRandomId());
subject = "Foo"; subject = "Foo";
timestamp = System.currentTimeMillis(); timestamp = System.currentTimeMillis();
size = 1234; size = 1234;
@@ -250,11 +246,11 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
oneOf(database).setSendability(txn, messageId, 1); oneOf(database).setSendability(txn, messageId, 1);
// The parent exists, is in the DB, and is in the same group // The parent exists, is in the DB, and is in the same group
oneOf(database).getGroupMessageParent(txn, messageId); oneOf(database).getGroupMessageParent(txn, messageId);
will(returnValue(parentId)); will(returnValue(messageId1));
// The parent is already sendable // The parent is already sendable
oneOf(database).getSendability(txn, parentId); oneOf(database).getSendability(txn, messageId1);
will(returnValue(1)); will(returnValue(1));
oneOf(database).setSendability(txn, parentId, 2); oneOf(database).setSendability(txn, messageId1, 2);
oneOf(database).commitTransaction(txn); oneOf(database).commitTransaction(txn);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, cleaner, DatabaseComponent db = createDatabaseComponent(database, cleaner,
@@ -288,13 +284,13 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
oneOf(database).setSendability(txn, messageId, 1); oneOf(database).setSendability(txn, messageId, 1);
// The parent exists, is in the DB, and is in the same group // The parent exists, is in the DB, and is in the same group
oneOf(database).getGroupMessageParent(txn, messageId); oneOf(database).getGroupMessageParent(txn, messageId);
will(returnValue(parentId)); will(returnValue(messageId1));
// The parent is not already sendable // The parent is not already sendable
oneOf(database).getSendability(txn, parentId); oneOf(database).getSendability(txn, messageId1);
will(returnValue(0)); will(returnValue(0));
oneOf(database).setSendability(txn, parentId, 1); oneOf(database).setSendability(txn, messageId1, 1);
// The parent has no parent // The parent has no parent
oneOf(database).getGroupMessageParent(txn, parentId); oneOf(database).getGroupMessageParent(txn, messageId1);
will(returnValue(null)); will(returnValue(null));
oneOf(database).commitTransaction(txn); oneOf(database).commitTransaction(txn);
}}); }});
@@ -494,7 +490,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
final ShutdownManager shutdown = context.mock(ShutdownManager.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class);
final PacketFactory packetFactory = context.mock(PacketFactory.class); final PacketFactory packetFactory = context.mock(PacketFactory.class);
final Ack ack = context.mock(Ack.class); final Ack ack = context.mock(Ack.class);
final Batch batch = context.mock(Batch.class);
final Offer offer = context.mock(Offer.class); final Offer offer = context.mock(Offer.class);
final SubscriptionUpdate subscriptionUpdate = final SubscriptionUpdate subscriptionUpdate =
context.mock(SubscriptionUpdate.class); context.mock(SubscriptionUpdate.class);
@@ -563,7 +558,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
} catch(NoSuchContactException expected) {} } catch(NoSuchContactException expected) {}
try { try {
db.receiveBatch(contactId, batch); db.receiveMessage(contactId, message);
fail(); fail();
} catch(NoSuchContactException expected) {} } catch(NoSuchContactException expected) {}
@@ -631,10 +626,8 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
@Test @Test
public void testGenerateAck() throws Exception { public void testGenerateAck() throws Exception {
final BatchId batchId1 = new BatchId(TestUtils.getRandomId()); final Collection<MessageId> messagesToAck = Arrays.asList(messageId,
final Collection<BatchId> batchesToAck = new ArrayList<BatchId>(); messageId1);
batchesToAck.add(batchId);
batchesToAck.add(batchId1);
Mockery context = new Mockery(); Mockery context = new Mockery();
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class); final Database<Object> database = context.mock(Database.class);
@@ -648,14 +641,14 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
allowing(database).commitTransaction(txn); allowing(database).commitTransaction(txn);
allowing(database).containsContact(txn, contactId); allowing(database).containsContact(txn, contactId);
will(returnValue(true)); will(returnValue(true));
// Get the batches to ack // Get the messages to ack
oneOf(database).getBatchesToAck(txn, contactId, 123); oneOf(database).getMessagesToAck(txn, contactId, 123);
will(returnValue(batchesToAck)); will(returnValue(messagesToAck));
// Create the packet // Create the ack packet
oneOf(packetFactory).createAck(batchesToAck); oneOf(packetFactory).createAck(messagesToAck);
will(returnValue(ack)); will(returnValue(ack));
// Record the batches that were acked // Record the messages that were acked
oneOf(database).removeBatchesToAck(txn, contactId, batchesToAck); oneOf(database).removeMessagesToAck(txn, contactId, messagesToAck);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, cleaner, DatabaseComponent db = createDatabaseComponent(database, cleaner,
shutdown, packetFactory); shutdown, packetFactory);
@@ -667,7 +660,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
@Test @Test
public void testGenerateBatch() throws Exception { public void testGenerateBatch() throws Exception {
final MessageId messageId1 = new MessageId(TestUtils.getRandomId());
final byte[] raw1 = new byte[size]; final byte[] raw1 = new byte[size];
final Collection<MessageId> sendable = Arrays.asList(messageId, final Collection<MessageId> sendable = Arrays.asList(messageId,
messageId1); messageId1);
@@ -678,7 +670,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class);
final PacketFactory packetFactory = context.mock(PacketFactory.class); final PacketFactory packetFactory = context.mock(PacketFactory.class);
final RawBatch batch = context.mock(RawBatch.class);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
allowing(database).startTransaction(); allowing(database).startTransaction();
will(returnValue(txn)); will(returnValue(txn));
@@ -692,40 +683,32 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
will(returnValue(raw)); will(returnValue(raw));
oneOf(database).getMessage(txn, messageId1); oneOf(database).getMessage(txn, messageId1);
will(returnValue(raw1)); will(returnValue(raw1));
// Create the packet // Record the outstanding messages
oneOf(packetFactory).createBatch(messages); oneOf(database).addOutstandingMessages(txn, contactId, sendable);
will(returnValue(batch));
// Record the outstanding batch
oneOf(batch).getId();
will(returnValue(batchId));
oneOf(database).addOutstandingBatch(txn, contactId, batchId,
sendable);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, cleaner, DatabaseComponent db = createDatabaseComponent(database, cleaner,
shutdown, packetFactory); shutdown, packetFactory);
assertEquals(batch, db.generateBatch(contactId, size * 2)); assertEquals(messages, db.generateBatch(contactId, size * 2));
context.assertIsSatisfied(); context.assertIsSatisfied();
} }
@Test @Test
public void testGenerateBatchFromRequest() throws Exception { public void testGenerateBatchFromRequest() throws Exception {
final MessageId messageId1 = new MessageId(TestUtils.getRandomId());
final MessageId messageId2 = new MessageId(TestUtils.getRandomId()); final MessageId messageId2 = new MessageId(TestUtils.getRandomId());
final byte[] raw1 = new byte[size]; final byte[] raw1 = new byte[size];
final Collection<MessageId> requested = new ArrayList<MessageId>(); final Collection<MessageId> requested = new ArrayList<MessageId>();
requested.add(messageId); requested.add(messageId);
requested.add(messageId1); requested.add(messageId1);
requested.add(messageId2); requested.add(messageId2);
final Collection<byte[]> msgs = Arrays.asList(raw1); final Collection<byte[]> messages = Arrays.asList(raw1);
Mockery context = new Mockery(); Mockery context = new Mockery();
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class); final Database<Object> database = context.mock(Database.class);
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class);
final PacketFactory packetFactory = context.mock(PacketFactory.class); final PacketFactory packetFactory = context.mock(PacketFactory.class);
final RawBatch batch = context.mock(RawBatch.class);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
allowing(database).startTransaction(); allowing(database).startTransaction();
will(returnValue(txn)); will(returnValue(txn));
@@ -739,19 +722,15 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
will(returnValue(raw1)); // Message is sendable will(returnValue(raw1)); // Message is sendable
oneOf(database).getMessageIfSendable(txn, contactId, messageId2); oneOf(database).getMessageIfSendable(txn, contactId, messageId2);
will(returnValue(null)); // Message is not sendable will(returnValue(null)); // Message is not sendable
// Create the packet // Record the outstanding messages
oneOf(packetFactory).createBatch(msgs); oneOf(database).addOutstandingMessages(txn, contactId,
will(returnValue(batch));
// Record the outstanding batch
oneOf(batch).getId();
will(returnValue(batchId));
oneOf(database).addOutstandingBatch(txn, contactId, batchId,
Collections.singletonList(messageId1)); Collections.singletonList(messageId1));
}}); }});
DatabaseComponent db = createDatabaseComponent(database, cleaner, DatabaseComponent db = createDatabaseComponent(database, cleaner,
shutdown, packetFactory); shutdown, packetFactory);
assertEquals(batch, db.generateBatch(contactId, size * 3, requested)); assertEquals(messages, db.generateBatch(contactId, size * 3,
requested));
context.assertIsSatisfied(); context.assertIsSatisfied();
} }
@@ -903,7 +882,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
@Test @Test
public void testReceiveAck() throws Exception { public void testReceiveAck() throws Exception {
final BatchId batchId1 = new BatchId(TestUtils.getRandomId());
Mockery context = new Mockery(); Mockery context = new Mockery();
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class); final Database<Object> database = context.mock(Database.class);
@@ -917,14 +895,14 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
allowing(database).commitTransaction(txn); allowing(database).commitTransaction(txn);
allowing(database).containsContact(txn, contactId); allowing(database).containsContact(txn, contactId);
will(returnValue(true)); will(returnValue(true));
// Get the acked batches // Get the acked messages
oneOf(ack).getBatchIds(); oneOf(ack).getMessageIds();
will(returnValue(Collections.singletonList(batchId))); will(returnValue(Collections.singletonList(messageId)));
oneOf(database).removeAckedBatch(txn, contactId, batchId); oneOf(database).removeAckedMessages(txn, contactId,
// Find lost batches Collections.singletonList(messageId));
oneOf(database).getLostBatches(txn, contactId); // Find lost messages
will(returnValue(Collections.singletonList(batchId1))); oneOf(database).getLostMessages(txn, contactId);
oneOf(database).removeLostBatch(txn, contactId, batchId1); will(returnValue(Collections.emptyList()));
}}); }});
DatabaseComponent db = createDatabaseComponent(database, cleaner, DatabaseComponent db = createDatabaseComponent(database, cleaner,
shutdown, packetFactory); shutdown, packetFactory);
@@ -935,74 +913,64 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
} }
@Test @Test
public void testReceiveBatchStoresPrivateMessage() throws Exception { public void testReceivePrivateMessage() throws Exception {
Mockery context = new Mockery(); Mockery context = new Mockery();
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class); final Database<Object> database = context.mock(Database.class);
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class);
final PacketFactory packetFactory = context.mock(PacketFactory.class); final PacketFactory packetFactory = context.mock(PacketFactory.class);
final Batch batch = context.mock(Batch.class);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
allowing(database).startTransaction(); allowing(database).startTransaction();
will(returnValue(txn)); will(returnValue(txn));
allowing(database).commitTransaction(txn); allowing(database).commitTransaction(txn);
allowing(database).containsContact(txn, contactId); allowing(database).containsContact(txn, contactId);
will(returnValue(true)); will(returnValue(true));
oneOf(batch).getMessages();
will(returnValue(Collections.singletonList(privateMessage)));
// The message is stored // The message is stored
oneOf(database).addPrivateMessage(txn, privateMessage, contactId); oneOf(database).addPrivateMessage(txn, privateMessage, contactId);
will(returnValue(true)); will(returnValue(true));
oneOf(database).setStatus(txn, contactId, messageId, Status.SEEN); oneOf(database).setStatus(txn, contactId, messageId, Status.SEEN);
// The batch must be acked // The message must be acked
oneOf(batch).getId(); oneOf(database).addMessageToAck(txn, contactId, messageId);
will(returnValue(batchId));
oneOf(database).addBatchToAck(txn, contactId, batchId);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, cleaner, DatabaseComponent db = createDatabaseComponent(database, cleaner,
shutdown, packetFactory); shutdown, packetFactory);
db.receiveBatch(contactId, batch); db.receiveMessage(contactId, privateMessage);
context.assertIsSatisfied(); context.assertIsSatisfied();
} }
@Test @Test
public void testReceiveBatchWithDuplicatePrivateMessage() throws Exception { public void testReceiveDuplicatePrivateMessage() throws Exception {
Mockery context = new Mockery(); Mockery context = new Mockery();
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class); final Database<Object> database = context.mock(Database.class);
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class);
final PacketFactory packetFactory = context.mock(PacketFactory.class); final PacketFactory packetFactory = context.mock(PacketFactory.class);
final Batch batch = context.mock(Batch.class);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
allowing(database).startTransaction(); allowing(database).startTransaction();
will(returnValue(txn)); will(returnValue(txn));
allowing(database).commitTransaction(txn); allowing(database).commitTransaction(txn);
allowing(database).containsContact(txn, contactId); allowing(database).containsContact(txn, contactId);
will(returnValue(true)); will(returnValue(true));
oneOf(batch).getMessages();
will(returnValue(Collections.singletonList(privateMessage)));
// The message is stored, but it's a duplicate // The message is stored, but it's a duplicate
oneOf(database).addPrivateMessage(txn, privateMessage, contactId); oneOf(database).addPrivateMessage(txn, privateMessage, contactId);
will(returnValue(false)); will(returnValue(false));
// The batch must still be acked // The message must still be acked
oneOf(batch).getId(); oneOf(database).addMessageToAck(txn, contactId, messageId);
will(returnValue(batchId));
oneOf(database).addBatchToAck(txn, contactId, batchId);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, cleaner, DatabaseComponent db = createDatabaseComponent(database, cleaner,
shutdown, packetFactory); shutdown, packetFactory);
db.receiveBatch(contactId, batch); db.receiveMessage(contactId, privateMessage);
context.assertIsSatisfied(); context.assertIsSatisfied();
} }
@Test @Test
public void testReceiveBatchDoesNotStoreGroupMessageUnlessSubscribed() public void testReceiveMessageDoesNotStoreGroupMessageUnlessSubscribed()
throws Exception { throws Exception {
Mockery context = new Mockery(); Mockery context = new Mockery();
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@@ -1010,7 +978,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class);
final PacketFactory packetFactory = context.mock(PacketFactory.class); final PacketFactory packetFactory = context.mock(PacketFactory.class);
final Batch batch = context.mock(Batch.class);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
allowing(database).startTransaction(); allowing(database).startTransaction();
will(returnValue(txn)); will(returnValue(txn));
@@ -1018,26 +985,22 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
allowing(database).containsContact(txn, contactId); allowing(database).containsContact(txn, contactId);
will(returnValue(true)); will(returnValue(true));
// Only store messages belonging to visible, subscribed groups // Only store messages belonging to visible, subscribed groups
oneOf(batch).getMessages();
will(returnValue(Collections.singletonList(message)));
oneOf(database).containsVisibleSubscription(txn, groupId, oneOf(database).containsVisibleSubscription(txn, groupId,
contactId, timestamp); contactId, timestamp);
will(returnValue(false)); will(returnValue(false));
// The message is not stored but the batch must still be acked // The message is not stored but it must still be acked
oneOf(batch).getId(); oneOf(database).addMessageToAck(txn, contactId, messageId);
will(returnValue(batchId));
oneOf(database).addBatchToAck(txn, contactId, batchId);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, cleaner, DatabaseComponent db = createDatabaseComponent(database, cleaner,
shutdown, packetFactory); shutdown, packetFactory);
db.receiveBatch(contactId, batch); db.receiveMessage(contactId, message);
context.assertIsSatisfied(); context.assertIsSatisfied();
} }
@Test @Test
public void testReceiveBatchDoesNotCalculateSendabilityForDuplicates() public void testReceiveMessageDoesNotCalculateSendabilityForDuplicates()
throws Exception { throws Exception {
Mockery context = new Mockery(); Mockery context = new Mockery();
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@@ -1045,7 +1008,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class);
final PacketFactory packetFactory = context.mock(PacketFactory.class); final PacketFactory packetFactory = context.mock(PacketFactory.class);
final Batch batch = context.mock(Batch.class);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
allowing(database).startTransaction(); allowing(database).startTransaction();
will(returnValue(txn)); will(returnValue(txn));
@@ -1053,8 +1015,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
allowing(database).containsContact(txn, contactId); allowing(database).containsContact(txn, contactId);
will(returnValue(true)); will(returnValue(true));
// Only store messages belonging to visible, subscribed groups // Only store messages belonging to visible, subscribed groups
oneOf(batch).getMessages();
will(returnValue(Collections.singletonList(message)));
oneOf(database).containsVisibleSubscription(txn, groupId, oneOf(database).containsVisibleSubscription(txn, groupId,
contactId, timestamp); contactId, timestamp);
will(returnValue(true)); will(returnValue(true));
@@ -1062,28 +1022,25 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
oneOf(database).addGroupMessage(txn, message); oneOf(database).addGroupMessage(txn, message);
will(returnValue(false)); will(returnValue(false));
oneOf(database).setStatus(txn, contactId, messageId, Status.SEEN); oneOf(database).setStatus(txn, contactId, messageId, Status.SEEN);
// The batch needs to be acknowledged // The message must be acked
oneOf(batch).getId(); oneOf(database).addMessageToAck(txn, contactId, messageId);
will(returnValue(batchId));
oneOf(database).addBatchToAck(txn, contactId, batchId);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, cleaner, DatabaseComponent db = createDatabaseComponent(database, cleaner,
shutdown, packetFactory); shutdown, packetFactory);
db.receiveBatch(contactId, batch); db.receiveMessage(contactId, message);
context.assertIsSatisfied(); context.assertIsSatisfied();
} }
@Test @Test
public void testReceiveBatchCalculatesSendability() throws Exception { public void testReceiveMessageCalculatesSendability() throws Exception {
Mockery context = new Mockery(); Mockery context = new Mockery();
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class); final Database<Object> database = context.mock(Database.class);
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class);
final PacketFactory packetFactory = context.mock(PacketFactory.class); final PacketFactory packetFactory = context.mock(PacketFactory.class);
final Batch batch = context.mock(Batch.class);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
allowing(database).startTransaction(); allowing(database).startTransaction();
will(returnValue(txn)); will(returnValue(txn));
@@ -1091,8 +1048,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
allowing(database).containsContact(txn, contactId); allowing(database).containsContact(txn, contactId);
will(returnValue(true)); will(returnValue(true));
// Only store messages belonging to visible, subscribed groups // Only store messages belonging to visible, subscribed groups
oneOf(batch).getMessages();
will(returnValue(Collections.singletonList(message)));
oneOf(database).containsVisibleSubscription(txn, groupId, oneOf(database).containsVisibleSubscription(txn, groupId,
contactId, timestamp); contactId, timestamp);
will(returnValue(true)); will(returnValue(true));
@@ -1109,28 +1064,26 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
oneOf(database).getNumberOfSendableChildren(txn, messageId); oneOf(database).getNumberOfSendableChildren(txn, messageId);
will(returnValue(0)); will(returnValue(0));
oneOf(database).setSendability(txn, messageId, 0); oneOf(database).setSendability(txn, messageId, 0);
// The batch needs to be acknowledged // The message must be acked
oneOf(batch).getId(); oneOf(database).addMessageToAck(txn, contactId, messageId);
will(returnValue(batchId));
oneOf(database).addBatchToAck(txn, contactId, batchId);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, cleaner, DatabaseComponent db = createDatabaseComponent(database, cleaner,
shutdown, packetFactory); shutdown, packetFactory);
db.receiveBatch(contactId, batch); db.receiveMessage(contactId, message);
context.assertIsSatisfied(); context.assertIsSatisfied();
} }
@Test @Test
public void testReceiveBatchUpdatesAncestorSendability() throws Exception { public void testReceiveMessageUpdatesAncestorSendability()
throws Exception {
Mockery context = new Mockery(); Mockery context = new Mockery();
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class); final Database<Object> database = context.mock(Database.class);
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class);
final PacketFactory packetFactory = context.mock(PacketFactory.class); final PacketFactory packetFactory = context.mock(PacketFactory.class);
final Batch batch = context.mock(Batch.class);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
allowing(database).startTransaction(); allowing(database).startTransaction();
will(returnValue(txn)); will(returnValue(txn));
@@ -1138,8 +1091,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
allowing(database).containsContact(txn, contactId); allowing(database).containsContact(txn, contactId);
will(returnValue(true)); will(returnValue(true));
// Only store messages belonging to visible, subscribed groups // Only store messages belonging to visible, subscribed groups
oneOf(batch).getMessages();
will(returnValue(Collections.singletonList(message)));
oneOf(database).containsVisibleSubscription(txn, groupId, oneOf(database).containsVisibleSubscription(txn, groupId,
contactId, timestamp); contactId, timestamp);
will(returnValue(true)); will(returnValue(true));
@@ -1158,15 +1109,13 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
oneOf(database).setSendability(txn, messageId, 2); oneOf(database).setSendability(txn, messageId, 2);
oneOf(database).getGroupMessageParent(txn, messageId); oneOf(database).getGroupMessageParent(txn, messageId);
will(returnValue(null)); will(returnValue(null));
// The batch needs to be acknowledged // The message must be acked
oneOf(batch).getId(); oneOf(database).addMessageToAck(txn, contactId, messageId);
will(returnValue(batchId));
oneOf(database).addBatchToAck(txn, contactId, batchId);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, cleaner, DatabaseComponent db = createDatabaseComponent(database, cleaner,
shutdown, packetFactory); shutdown, packetFactory);
db.receiveBatch(contactId, batch); db.receiveMessage(contactId, message);
context.assertIsSatisfied(); context.assertIsSatisfied();
} }
@@ -1319,7 +1268,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
oneOf(database).setSendability(txn, messageId, 0); oneOf(database).setSendability(txn, messageId, 0);
oneOf(database).commitTransaction(txn); oneOf(database).commitTransaction(txn);
// The message was added, so the listener should be called // The message was added, so the listener should be called
oneOf(listener).eventOccurred(with(any(MessagesAddedEvent.class))); oneOf(listener).eventOccurred(with(any(MessageAddedEvent.class)));
}}); }});
DatabaseComponent db = createDatabaseComponent(database, cleaner, DatabaseComponent db = createDatabaseComponent(database, cleaner,
shutdown, packetFactory); shutdown, packetFactory);
@@ -1350,7 +1299,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
will(returnValue(true)); will(returnValue(true));
oneOf(database).setStatus(txn, contactId, messageId, Status.NEW); oneOf(database).setStatus(txn, contactId, messageId, Status.NEW);
// The message was added, so the listener should be called // The message was added, so the listener should be called
oneOf(listener).eventOccurred(with(any(MessagesAddedEvent.class))); oneOf(listener).eventOccurred(with(any(MessageAddedEvent.class)));
}}); }});
DatabaseComponent db = createDatabaseComponent(database, cleaner, DatabaseComponent db = createDatabaseComponent(database, cleaner,
shutdown, packetFactory); shutdown, packetFactory);

View File

@@ -1,7 +1,6 @@
package net.sf.briar.db; package net.sf.briar.db;
import static java.util.concurrent.TimeUnit.SECONDS; import static java.util.concurrent.TimeUnit.SECONDS;
import static net.sf.briar.db.DatabaseConstants.RETRANSMIT_THRESHOLD;
import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertArrayEquals;
import java.io.File; import java.io.File;
@@ -20,6 +19,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
import net.sf.briar.BriarTestCase; import net.sf.briar.BriarTestCase;
import net.sf.briar.TestDatabaseConfig; import net.sf.briar.TestDatabaseConfig;
import net.sf.briar.TestMessage;
import net.sf.briar.TestUtils; import net.sf.briar.TestUtils;
import net.sf.briar.api.ContactId; import net.sf.briar.api.ContactId;
import net.sf.briar.api.Rating; import net.sf.briar.api.Rating;
@@ -29,7 +29,6 @@ import net.sf.briar.api.clock.SystemClock;
import net.sf.briar.api.db.DbException; import net.sf.briar.api.db.DbException;
import net.sf.briar.api.db.MessageHeader; import net.sf.briar.api.db.MessageHeader;
import net.sf.briar.api.protocol.AuthorId; import net.sf.briar.api.protocol.AuthorId;
import net.sf.briar.api.protocol.BatchId;
import net.sf.briar.api.protocol.Group; import net.sf.briar.api.protocol.Group;
import net.sf.briar.api.protocol.GroupId; import net.sf.briar.api.protocol.GroupId;
import net.sf.briar.api.protocol.Message; import net.sf.briar.api.protocol.Message;
@@ -53,10 +52,9 @@ public class H2DatabaseTest extends BriarTestCase {
private final Random random = new Random(); private final Random random = new Random();
private final Group group; private final Group group;
private final AuthorId authorId; private final AuthorId authorId;
private final BatchId batchId;
private final ContactId contactId; private final ContactId contactId;
private final GroupId groupId; private final GroupId groupId;
private final MessageId messageId, privateMessageId; private final MessageId messageId, messageId1;
private final String subject; private final String subject;
private final long timestamp; private final long timestamp;
private final int size; private final int size;
@@ -67,11 +65,10 @@ public class H2DatabaseTest extends BriarTestCase {
public H2DatabaseTest() throws Exception { public H2DatabaseTest() throws Exception {
super(); super();
authorId = new AuthorId(TestUtils.getRandomId()); authorId = new AuthorId(TestUtils.getRandomId());
batchId = new BatchId(TestUtils.getRandomId());
contactId = new ContactId(1); contactId = new ContactId(1);
groupId = new GroupId(TestUtils.getRandomId()); groupId = new GroupId(TestUtils.getRandomId());
messageId = new MessageId(TestUtils.getRandomId()); messageId = new MessageId(TestUtils.getRandomId());
privateMessageId = new MessageId(TestUtils.getRandomId()); messageId1 = new MessageId(TestUtils.getRandomId());
group = new Group(groupId, "Foo", null); group = new Group(groupId, "Foo", null);
subject = "Foo"; subject = "Foo";
timestamp = System.currentTimeMillis(); timestamp = System.currentTimeMillis();
@@ -80,7 +77,7 @@ public class H2DatabaseTest extends BriarTestCase {
random.nextBytes(raw); random.nextBytes(raw);
message = new TestMessage(messageId, null, groupId, authorId, subject, message = new TestMessage(messageId, null, groupId, authorId, subject,
timestamp, raw); timestamp, raw);
privateMessage = new TestMessage(privateMessageId, null, null, null, privateMessage = new TestMessage(messageId1, null, null, null,
subject, timestamp, raw); subject, timestamp, raw);
transportId = new TransportId(TestUtils.getRandomId()); transportId = new TransportId(TestUtils.getRandomId());
} }
@@ -104,9 +101,9 @@ public class H2DatabaseTest extends BriarTestCase {
assertFalse(db.containsMessage(txn, messageId)); assertFalse(db.containsMessage(txn, messageId));
db.addGroupMessage(txn, message); db.addGroupMessage(txn, message);
assertTrue(db.containsMessage(txn, messageId)); assertTrue(db.containsMessage(txn, messageId));
assertFalse(db.containsMessage(txn, privateMessageId)); assertFalse(db.containsMessage(txn, messageId1));
db.addPrivateMessage(txn, privateMessage, contactId); db.addPrivateMessage(txn, privateMessage, contactId);
assertTrue(db.containsMessage(txn, privateMessageId)); assertTrue(db.containsMessage(txn, messageId1));
db.commitTransaction(txn); db.commitTransaction(txn);
db.close(); db.close();
@@ -118,12 +115,12 @@ public class H2DatabaseTest extends BriarTestCase {
assertTrue(db.containsMessage(txn, messageId)); assertTrue(db.containsMessage(txn, messageId));
byte[] raw1 = db.getMessage(txn, messageId); byte[] raw1 = db.getMessage(txn, messageId);
assertArrayEquals(raw, raw1); assertArrayEquals(raw, raw1);
assertTrue(db.containsMessage(txn, privateMessageId)); assertTrue(db.containsMessage(txn, messageId1));
raw1 = db.getMessage(txn, privateMessageId); raw1 = db.getMessage(txn, messageId1);
assertArrayEquals(raw, raw1); assertArrayEquals(raw, raw1);
// Delete the records // Delete the records
db.removeMessage(txn, messageId); db.removeMessage(txn, messageId);
db.removeMessage(txn, privateMessageId); db.removeMessage(txn, messageId1);
db.removeContact(txn, contactId); db.removeContact(txn, contactId);
db.removeSubscription(txn, groupId); db.removeSubscription(txn, groupId);
db.commitTransaction(txn); db.commitTransaction(txn);
@@ -137,7 +134,7 @@ public class H2DatabaseTest extends BriarTestCase {
db.getRemoteProperties(txn, transportId)); db.getRemoteProperties(txn, transportId));
assertFalse(db.containsSubscription(txn, groupId)); assertFalse(db.containsSubscription(txn, groupId));
assertFalse(db.containsMessage(txn, messageId)); assertFalse(db.containsMessage(txn, messageId));
assertFalse(db.containsMessage(txn, privateMessageId)); assertFalse(db.containsMessage(txn, messageId1));
db.commitTransaction(txn); db.commitTransaction(txn);
db.close(); db.close();
} }
@@ -216,9 +213,9 @@ public class H2DatabaseTest extends BriarTestCase {
db.addPrivateMessage(txn, privateMessage, contactId); db.addPrivateMessage(txn, privateMessage, contactId);
// Removing the contact should remove the message // Removing the contact should remove the message
assertTrue(db.containsMessage(txn, privateMessageId)); assertTrue(db.containsMessage(txn, messageId1));
db.removeContact(txn, contactId); db.removeContact(txn, contactId);
assertFalse(db.containsMessage(txn, privateMessageId)); assertFalse(db.containsMessage(txn, messageId1));
db.commitTransaction(txn); db.commitTransaction(txn);
db.close(); db.close();
@@ -241,21 +238,21 @@ public class H2DatabaseTest extends BriarTestCase {
assertFalse(it.hasNext()); assertFalse(it.hasNext());
// Changing the status to NEW should make the message sendable // Changing the status to NEW should make the message sendable
db.setStatus(txn, contactId, privateMessageId, Status.NEW); db.setStatus(txn, contactId, messageId1, Status.NEW);
assertTrue(db.hasSendableMessages(txn, contactId)); assertTrue(db.hasSendableMessages(txn, contactId));
it = db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator(); it = db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator();
assertTrue(it.hasNext()); assertTrue(it.hasNext());
assertEquals(privateMessageId, it.next()); assertEquals(messageId1, it.next());
assertFalse(it.hasNext()); assertFalse(it.hasNext());
// Changing the status to SENT should make the message unsendable // Changing the status to SENT should make the message unsendable
db.setStatus(txn, contactId, privateMessageId, Status.SENT); db.setStatus(txn, contactId, messageId1, Status.SENT);
assertFalse(db.hasSendableMessages(txn, contactId)); assertFalse(db.hasSendableMessages(txn, contactId));
it = db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator(); it = db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator();
assertFalse(it.hasNext()); assertFalse(it.hasNext());
// Changing the status to SEEN should also make the message unsendable // Changing the status to SEEN should also make the message unsendable
db.setStatus(txn, contactId, privateMessageId, Status.SEEN); db.setStatus(txn, contactId, messageId1, Status.SEEN);
it = db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator(); it = db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator();
assertFalse(it.hasNext()); assertFalse(it.hasNext());
@@ -272,7 +269,7 @@ public class H2DatabaseTest extends BriarTestCase {
// Add a contact and store a private message // Add a contact and store a private message
assertEquals(contactId, db.addContact(txn)); assertEquals(contactId, db.addContact(txn));
db.addPrivateMessage(txn, privateMessage, contactId); db.addPrivateMessage(txn, privateMessage, contactId);
db.setStatus(txn, contactId, privateMessageId, Status.NEW); db.setStatus(txn, contactId, messageId1, Status.NEW);
// The message is sendable, but too large to send // The message is sendable, but too large to send
assertTrue(db.hasSendableMessages(txn, contactId)); assertTrue(db.hasSendableMessages(txn, contactId));
@@ -284,7 +281,7 @@ public class H2DatabaseTest extends BriarTestCase {
assertTrue(db.hasSendableMessages(txn, contactId)); assertTrue(db.hasSendableMessages(txn, contactId));
it = db.getSendableMessages(txn, contactId, size).iterator(); it = db.getSendableMessages(txn, contactId, size).iterator();
assertTrue(it.hasNext()); assertTrue(it.hasNext());
assertEquals(privateMessageId, it.next()); assertEquals(messageId1, it.next());
assertFalse(it.hasNext()); assertFalse(it.hasNext());
db.commitTransaction(txn); db.commitTransaction(txn);
@@ -508,109 +505,58 @@ public class H2DatabaseTest extends BriarTestCase {
} }
@Test @Test
public void testBatchesToAck() throws Exception { public void testMessagesToAck() throws Exception {
BatchId batchId1 = new BatchId(TestUtils.getRandomId());
Database<Connection> db = open(false); Database<Connection> db = open(false);
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact and some batches to ack // Add a contact and some messages to ack
assertEquals(contactId, db.addContact(txn)); assertEquals(contactId, db.addContact(txn));
db.addBatchToAck(txn, contactId, batchId); db.addMessageToAck(txn, contactId, messageId);
db.addBatchToAck(txn, contactId, batchId1); db.addMessageToAck(txn, contactId, messageId1);
// Both batch IDs should be returned // Both message IDs should be returned
Collection<BatchId> acks = db.getBatchesToAck(txn, contactId, 1234); Collection<MessageId> ids = Arrays.asList(messageId, messageId1);
assertEquals(2, acks.size()); assertEquals(ids, db.getMessagesToAck(txn, contactId, 1234));
assertTrue(acks.contains(batchId));
assertTrue(acks.contains(batchId1));
// Remove the batch IDs // Remove both message IDs
db.removeBatchesToAck(txn, contactId, acks); db.removeMessagesToAck(txn, contactId, ids);
// Both batch IDs should have been removed // Both message IDs should have been removed
acks = db.getBatchesToAck(txn, contactId, 1234); assertEquals(Collections.emptyList(), db.getMessagesToAck(txn,
assertEquals(0, acks.size()); contactId, 1234));
db.commitTransaction(txn); db.commitTransaction(txn);
db.close(); db.close();
} }
@Test @Test
public void testDuplicateBatchesReceived() throws Exception { public void testDuplicateMessageReceived() throws Exception {
Database<Connection> db = open(false); Database<Connection> db = open(false);
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact and receive the same batch twice // Add a contact and receive the same message twice
assertEquals(contactId, db.addContact(txn)); assertEquals(contactId, db.addContact(txn));
db.addBatchToAck(txn, contactId, batchId); db.addMessageToAck(txn, contactId, messageId);
db.addBatchToAck(txn, contactId, batchId); db.addMessageToAck(txn, contactId, messageId);
// The batch ID should only be returned once // The message ID should only be returned once
Collection<BatchId> acks = db.getBatchesToAck(txn, contactId, 1234); Collection<MessageId> ids = db.getMessagesToAck(txn, contactId, 1234);
assertEquals(1, acks.size()); assertEquals(Collections.singletonList(messageId), ids);
assertTrue(acks.contains(batchId));
// Remove the batch ID // Remove the message ID
db.removeBatchesToAck(txn, contactId, acks); db.removeMessagesToAck(txn, contactId,
Collections.singletonList(messageId));
// The batch ID should have been removed // The message ID should have been removed
acks = db.getBatchesToAck(txn, contactId, 1234); assertEquals(Collections.emptyList(), db.getMessagesToAck(txn,
assertEquals(0, acks.size()); contactId, 1234));
db.commitTransaction(txn); db.commitTransaction(txn);
db.close(); db.close();
} }
@Test @Test
public void testSameBatchCannotBeSentTwice() throws Exception { public void testRemoveAckedMessage() throws Exception {
Database<Connection> db = open(false);
Connection txn = db.startTransaction();
// Add a contact, subscribe to a group and store a message
assertEquals(contactId, db.addContact(txn));
db.addSubscription(txn, group);
db.addGroupMessage(txn, message);
// Add an outstanding batch
db.addOutstandingBatch(txn, contactId, batchId,
Collections.singletonList(messageId));
// It should not be possible to add the same outstanding batch again
try {
db.addOutstandingBatch(txn, contactId, batchId,
Collections.singletonList(messageId));
fail();
} catch(DbException expected) {}
db.abortTransaction(txn);
db.close();
}
@Test
public void testSameBatchCanBeSentToDifferentContacts() throws Exception {
Database<Connection> db = open(false);
Connection txn = db.startTransaction();
// Add two contacts, subscribe to a group and store a message
assertEquals(contactId, db.addContact(txn));
ContactId contactId1 = db.addContact(txn);
db.addSubscription(txn, group);
db.addGroupMessage(txn, message);
// Add an outstanding batch for the first contact
db.addOutstandingBatch(txn, contactId, batchId,
Collections.singletonList(messageId));
// Add the same outstanding batch for the second contact
db.addOutstandingBatch(txn, contactId1, batchId,
Collections.singletonList(messageId));
db.commitTransaction(txn);
db.close();
}
@Test
public void testRemoveAckedBatch() throws Exception {
Database<Connection> db = open(false); Database<Connection> db = open(false);
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
@@ -630,15 +576,16 @@ public class H2DatabaseTest extends BriarTestCase {
assertEquals(messageId, it.next()); assertEquals(messageId, it.next());
assertFalse(it.hasNext()); assertFalse(it.hasNext());
db.setStatus(txn, contactId, messageId, Status.SENT); db.setStatus(txn, contactId, messageId, Status.SENT);
db.addOutstandingBatch(txn, contactId, batchId, db.addOutstandingMessages(txn, contactId,
Collections.singletonList(messageId)); Collections.singletonList(messageId));
// The message should no longer be sendable // The message should no longer be sendable
it = db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator(); it = db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator();
assertFalse(it.hasNext()); assertFalse(it.hasNext());
// Pretend that the batch was acked // Pretend that the message was acked
db.removeAckedBatch(txn, contactId, batchId); db.removeAckedMessages(txn, contactId,
Collections.singletonList(messageId));
// The message still should not be sendable // The message still should not be sendable
it = db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator(); it = db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator();
@@ -649,7 +596,7 @@ public class H2DatabaseTest extends BriarTestCase {
} }
@Test @Test
public void testRemoveLostBatch() throws Exception { public void testRemoveLostMessage() throws Exception {
Database<Connection> db = open(false); Database<Connection> db = open(false);
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
@@ -669,15 +616,16 @@ public class H2DatabaseTest extends BriarTestCase {
assertEquals(messageId, it.next()); assertEquals(messageId, it.next());
assertFalse(it.hasNext()); assertFalse(it.hasNext());
db.setStatus(txn, contactId, messageId, Status.SENT); db.setStatus(txn, contactId, messageId, Status.SENT);
db.addOutstandingBatch(txn, contactId, batchId, db.addOutstandingMessages(txn, contactId,
Collections.singletonList(messageId)); Collections.singletonList(messageId));
// The message should no longer be sendable // The message should no longer be sendable
it = db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator(); it = db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator();
assertFalse(it.hasNext()); assertFalse(it.hasNext());
// Pretend that the batch was lost // Pretend that the message was lost
db.removeLostBatch(txn, contactId, batchId); db.removeLostMessages(txn, contactId,
Collections.singletonList(messageId));
// The message should be sendable again // The message should be sendable again
it = db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator(); it = db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator();
@@ -689,77 +637,6 @@ public class H2DatabaseTest extends BriarTestCase {
db.close(); db.close();
} }
@Test
public void testRetransmission() throws Exception {
BatchId[] ids = new BatchId[RETRANSMIT_THRESHOLD + 5];
for(int i = 0; i < ids.length; i++) {
ids[i] = new BatchId(TestUtils.getRandomId());
}
Database<Connection> db = open(false);
Connection txn = db.startTransaction();
// Add a contact
assertEquals(contactId, db.addContact(txn));
// Add some outstanding batches, a few ms apart
for(int i = 0; i < ids.length; i++) {
db.addOutstandingBatch(txn, contactId, ids[i],
Collections.<MessageId>emptyList());
Thread.sleep(5);
}
// The contact acks the batches in reverse order. The first
// RETRANSMIT_THRESHOLD - 1 acks should not trigger any retransmissions
for(int i = 0; i < RETRANSMIT_THRESHOLD - 1; i++) {
db.removeAckedBatch(txn, contactId, ids[ids.length - i - 1]);
Collection<BatchId> lost = db.getLostBatches(txn, contactId);
assertEquals(Collections.emptyList(), lost);
}
// The next ack should trigger the retransmission of the remaining
// five outstanding batches
int index = ids.length - RETRANSMIT_THRESHOLD;
db.removeAckedBatch(txn, contactId, ids[index]);
Collection<BatchId> lost = db.getLostBatches(txn, contactId);
for(int i = 0; i < index; i++) {
assertTrue(lost.contains(ids[i]));
}
db.commitTransaction(txn);
db.close();
}
@Test
public void testNoRetransmission() throws Exception {
BatchId[] ids = new BatchId[RETRANSMIT_THRESHOLD * 2];
for(int i = 0; i < ids.length; i++) {
ids[i] = new BatchId(TestUtils.getRandomId());
}
Database<Connection> db = open(false);
Connection txn = db.startTransaction();
// Add a contact
assertEquals(contactId, db.addContact(txn));
// Add some outstanding batches, a few ms apart
for(int i = 0; i < ids.length; i++) {
db.addOutstandingBatch(txn, contactId, ids[i],
Collections.<MessageId>emptyList());
Thread.sleep(5);
}
// The contact acks the batches in the order they were sent - nothing
// should be retransmitted
for(int i = 0; i < ids.length; i++) {
db.removeAckedBatch(txn, contactId, ids[i]);
Collection<BatchId> lost = db.getLostBatches(txn, contactId);
assertEquals(Collections.emptyList(), lost);
}
db.commitTransaction(txn);
db.close();
}
@Test @Test
public void testGetMessagesByAuthor() throws Exception { public void testGetMessagesByAuthor() throws Exception {
AuthorId authorId1 = new AuthorId(TestUtils.getRandomId()); AuthorId authorId1 = new AuthorId(TestUtils.getRandomId());
@@ -1426,12 +1303,12 @@ public class H2DatabaseTest extends BriarTestCase {
// A message with a private parent should return null // A message with a private parent should return null
MessageId childId = new MessageId(TestUtils.getRandomId()); MessageId childId = new MessageId(TestUtils.getRandomId());
Message child = new TestMessage(childId, privateMessageId, groupId, Message child = new TestMessage(childId, messageId1, groupId,
null, subject, timestamp, raw); null, subject, timestamp, raw);
db.addGroupMessage(txn, child); db.addGroupMessage(txn, child);
db.addPrivateMessage(txn, privateMessage, contactId); db.addPrivateMessage(txn, privateMessage, contactId);
assertTrue(db.containsMessage(txn, childId)); assertTrue(db.containsMessage(txn, childId));
assertTrue(db.containsMessage(txn, privateMessageId)); assertTrue(db.containsMessage(txn, messageId1));
assertNull(db.getGroupMessageParent(txn, childId)); assertNull(db.getGroupMessageParent(txn, childId));
db.commitTransaction(txn); db.commitTransaction(txn);
@@ -1477,7 +1354,7 @@ public class H2DatabaseTest extends BriarTestCase {
int bodyLength = raw.length - 20; int bodyLength = raw.length - 20;
Message message1 = new TestMessage(messageId, null, groupId, null, Message message1 = new TestMessage(messageId, null, groupId, null,
subject, timestamp, raw, 5, bodyLength); subject, timestamp, raw, 5, bodyLength);
Message privateMessage1 = new TestMessage(privateMessageId, null, null, Message privateMessage1 = new TestMessage(messageId1, null, null,
null, subject, timestamp, raw, 10, bodyLength); null, subject, timestamp, raw, 10, bodyLength);
db.addGroupMessage(txn, message1); db.addGroupMessage(txn, message1);
db.addPrivateMessage(txn, privateMessage1, contactId); db.addPrivateMessage(txn, privateMessage1, contactId);
@@ -1492,12 +1369,12 @@ public class H2DatabaseTest extends BriarTestCase {
// Retrieve the raw messages // Retrieve the raw messages
assertArrayEquals(raw, db.getMessage(txn, messageId)); assertArrayEquals(raw, db.getMessage(txn, messageId));
assertArrayEquals(raw, db.getMessage(txn, privateMessageId)); assertArrayEquals(raw, db.getMessage(txn, messageId1));
// Retrieve the message bodies // Retrieve the message bodies
byte[] body = db.getMessageBody(txn, messageId); byte[] body = db.getMessageBody(txn, messageId);
assertArrayEquals(expectedBody, body); assertArrayEquals(expectedBody, body);
byte[] body1 = db.getMessageBody(txn, privateMessageId); byte[] body1 = db.getMessageBody(txn, messageId1);
assertArrayEquals(expectedBody1, body1); assertArrayEquals(expectedBody1, body1);
db.commitTransaction(txn); db.commitTransaction(txn);

View File

@@ -1,137 +0,0 @@
package net.sf.briar.protocol;
import static net.sf.briar.api.protocol.ProtocolConstants.MAX_PACKET_LENGTH;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Collections;
import net.sf.briar.BriarTestCase;
import net.sf.briar.api.FormatException;
import net.sf.briar.api.protocol.Types;
import net.sf.briar.api.protocol.UnverifiedBatch;
import net.sf.briar.api.serial.Reader;
import net.sf.briar.api.serial.ReaderFactory;
import net.sf.briar.api.serial.StructReader;
import net.sf.briar.api.serial.Writer;
import net.sf.briar.api.serial.WriterFactory;
import net.sf.briar.serial.SerialModule;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.junit.Test;
import com.google.inject.Guice;
import com.google.inject.Injector;
public class BatchReaderTest extends BriarTestCase {
// FIXME: This is an integration test, not a unit test
private final ReaderFactory readerFactory;
private final WriterFactory writerFactory;
private final Mockery context;
private final UnverifiedMessage message;
private final StructReader<UnverifiedMessage> messageReader;
public BatchReaderTest() throws Exception {
super();
Injector i = Guice.createInjector(new SerialModule());
readerFactory = i.getInstance(ReaderFactory.class);
writerFactory = i.getInstance(WriterFactory.class);
context = new Mockery();
message = context.mock(UnverifiedMessage.class);
messageReader = new TestMessageReader();
}
@Test
public void testFormatExceptionIfBatchIsTooLarge() throws Exception {
UnverifiedBatchFactory batchFactory =
context.mock(UnverifiedBatchFactory.class);
BatchReader batchReader = new BatchReader(messageReader, batchFactory);
byte[] b = createBatch(MAX_PACKET_LENGTH + 1);
ByteArrayInputStream in = new ByteArrayInputStream(b);
Reader reader = readerFactory.createReader(in);
reader.addStructReader(Types.BATCH, batchReader);
try {
reader.readStruct(Types.BATCH, UnverifiedBatch.class);
fail();
} catch(FormatException expected) {}
context.assertIsSatisfied();
}
@Test
public void testNoFormatExceptionIfBatchIsMaximumSize() throws Exception {
final UnverifiedBatchFactory batchFactory =
context.mock(UnverifiedBatchFactory.class);
BatchReader batchReader = new BatchReader(messageReader, batchFactory);
final UnverifiedBatch batch = context.mock(UnverifiedBatch.class);
context.checking(new Expectations() {{
oneOf(batchFactory).createUnverifiedBatch(
Collections.singletonList(message));
will(returnValue(batch));
}});
byte[] b = createBatch(MAX_PACKET_LENGTH);
ByteArrayInputStream in = new ByteArrayInputStream(b);
Reader reader = readerFactory.createReader(in);
reader.addStructReader(Types.BATCH, batchReader);
assertEquals(batch, reader.readStruct(Types.BATCH,
UnverifiedBatch.class));
context.assertIsSatisfied();
}
@Test
public void testEmptyBatch() throws Exception {
final UnverifiedBatchFactory batchFactory =
context.mock(UnverifiedBatchFactory.class);
BatchReader batchReader = new BatchReader(messageReader, batchFactory);
byte[] b = createEmptyBatch();
ByteArrayInputStream in = new ByteArrayInputStream(b);
Reader reader = readerFactory.createReader(in);
reader.addStructReader(Types.BATCH, batchReader);
try {
reader.readStruct(Types.BATCH, UnverifiedBatch.class);
fail();
} catch(FormatException expected) {}
context.assertIsSatisfied();
}
private byte[] createBatch(int size) throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream(size);
Writer w = writerFactory.createWriter(out);
w.writeStructId(Types.BATCH);
w.writeListStart();
// We're using a fake message reader, so it's OK to use a fake message
w.writeStructId(Types.MESSAGE);
w.writeBytes(new byte[size - 10]);
w.writeListEnd();
byte[] b = out.toByteArray();
assertEquals(size, b.length);
return b;
}
private byte[] createEmptyBatch() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
Writer w = writerFactory.createWriter(out);
w.writeStructId(Types.BATCH);
w.writeListStart();
w.writeListEnd();
return out.toByteArray();
}
private class TestMessageReader implements StructReader<UnverifiedMessage> {
public UnverifiedMessage readStruct(Reader r) throws IOException {
r.readStructId(Types.MESSAGE);
r.readBytes();
return message;
}
}
}

View File

@@ -14,7 +14,6 @@ import java.io.ByteArrayOutputStream;
import java.security.PrivateKey; import java.security.PrivateKey;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.Map; import java.util.Map;
import net.sf.briar.BriarTestCase; import net.sf.briar.BriarTestCase;
@@ -23,7 +22,6 @@ import net.sf.briar.api.crypto.CryptoComponent;
import net.sf.briar.api.protocol.Ack; import net.sf.briar.api.protocol.Ack;
import net.sf.briar.api.protocol.Author; import net.sf.briar.api.protocol.Author;
import net.sf.briar.api.protocol.AuthorFactory; import net.sf.briar.api.protocol.AuthorFactory;
import net.sf.briar.api.protocol.BatchId;
import net.sf.briar.api.protocol.Group; import net.sf.briar.api.protocol.Group;
import net.sf.briar.api.protocol.GroupFactory; import net.sf.briar.api.protocol.GroupFactory;
import net.sf.briar.api.protocol.Message; import net.sf.briar.api.protocol.Message;
@@ -33,7 +31,6 @@ import net.sf.briar.api.protocol.Offer;
import net.sf.briar.api.protocol.PacketFactory; import net.sf.briar.api.protocol.PacketFactory;
import net.sf.briar.api.protocol.ProtocolWriter; import net.sf.briar.api.protocol.ProtocolWriter;
import net.sf.briar.api.protocol.ProtocolWriterFactory; import net.sf.briar.api.protocol.ProtocolWriterFactory;
import net.sf.briar.api.protocol.RawBatch;
import net.sf.briar.api.protocol.Transport; import net.sf.briar.api.protocol.Transport;
import net.sf.briar.api.protocol.TransportId; import net.sf.briar.api.protocol.TransportId;
import net.sf.briar.api.protocol.TransportUpdate; import net.sf.briar.api.protocol.TransportUpdate;
@@ -69,24 +66,24 @@ public class ConstantsTest extends BriarTestCase {
} }
@Test @Test
public void testBatchesFitIntoLargeAck() throws Exception { public void testMessageIdsFitIntoLargeAck() throws Exception {
testBatchesFitIntoAck(MAX_PACKET_LENGTH); testMessageIdsFitIntoAck(MAX_PACKET_LENGTH);
} }
@Test @Test
public void testBatchesFitIntoSmallAck() throws Exception { public void testMessageIdsFitIntoSmallAck() throws Exception {
testBatchesFitIntoAck(1000); testMessageIdsFitIntoAck(1000);
} }
private void testBatchesFitIntoAck(int length) throws Exception { private void testMessageIdsFitIntoAck(int length) throws Exception {
// Create an ack with as many batch IDs as possible // Create an ack with as many message IDs as possible
ByteArrayOutputStream out = new ByteArrayOutputStream(length); ByteArrayOutputStream out = new ByteArrayOutputStream(length);
ProtocolWriter writer = protocolWriterFactory.createProtocolWriter(out, ProtocolWriter writer = protocolWriterFactory.createProtocolWriter(out,
true); true);
int maxBatches = writer.getMaxBatchesForAck(length); int maxMessages = writer.getMaxMessagesForAck(length);
Collection<BatchId> acked = new ArrayList<BatchId>(); Collection<MessageId> acked = new ArrayList<MessageId>();
for(int i = 0; i < maxBatches; i++) { for(int i = 0; i < maxMessages; i++) {
acked.add(new BatchId(TestUtils.getRandomId())); acked.add(new MessageId(TestUtils.getRandomId()));
} }
Ack a = packetFactory.createAck(acked); Ack a = packetFactory.createAck(acked);
writer.writeAck(a); writer.writeAck(a);
@@ -95,7 +92,7 @@ public class ConstantsTest extends BriarTestCase {
} }
@Test @Test
public void testMessageFitsIntoBatch() throws Exception { public void testMessageFitsIntoPacket() throws Exception {
// Create a maximum-length group // Create a maximum-length group
String groupName = createRandomString(MAX_GROUP_NAME_LENGTH); String groupName = createRandomString(MAX_GROUP_NAME_LENGTH);
byte[] groupPublic = new byte[MAX_PUBLIC_KEY_LENGTH]; byte[] groupPublic = new byte[MAX_PUBLIC_KEY_LENGTH];
@@ -111,32 +108,25 @@ public class ConstantsTest extends BriarTestCase {
byte[] body = new byte[MAX_BODY_LENGTH]; byte[] body = new byte[MAX_BODY_LENGTH];
Message message = messageFactory.createMessage(null, group, Message message = messageFactory.createMessage(null, group,
groupPrivate, author, authorPrivate, subject, body); groupPrivate, author, authorPrivate, subject, body);
// Add the message to a batch // Check the size of the serialised message
ByteArrayOutputStream out = int length = message.getSerialised().length;
new ByteArrayOutputStream(MAX_PACKET_LENGTH); assertTrue(length > UniqueId.LENGTH + MAX_GROUP_NAME_LENGTH
ProtocolWriter writer = protocolWriterFactory.createProtocolWriter(out,
true);
RawBatch b = packetFactory.createBatch(Collections.singletonList(
message.getSerialised()));
writer.writeBatch(b);
// Check the size of the serialised batch
assertTrue(out.size() > UniqueId.LENGTH + MAX_GROUP_NAME_LENGTH
+ MAX_PUBLIC_KEY_LENGTH + MAX_AUTHOR_NAME_LENGTH + MAX_PUBLIC_KEY_LENGTH + MAX_AUTHOR_NAME_LENGTH
+ MAX_PUBLIC_KEY_LENGTH + MAX_BODY_LENGTH); + MAX_PUBLIC_KEY_LENGTH + MAX_BODY_LENGTH);
assertTrue(out.size() <= MAX_PACKET_LENGTH); assertTrue(length <= MAX_PACKET_LENGTH);
} }
@Test @Test
public void testMessagesFitIntoLargeOffer() throws Exception { public void testMessageIdsFitIntoLargeOffer() throws Exception {
testMessagesFitIntoOffer(MAX_PACKET_LENGTH); testMessageIdsFitIntoOffer(MAX_PACKET_LENGTH);
} }
@Test @Test
public void testMessagesFitIntoSmallOffer() throws Exception { public void testMessageIdsFitIntoSmallOffer() throws Exception {
testMessagesFitIntoOffer(1000); testMessageIdsFitIntoOffer(1000);
} }
private void testMessagesFitIntoOffer(int length) throws Exception { private void testMessageIdsFitIntoOffer(int length) throws Exception {
// Create an offer with as many message IDs as possible // Create an offer with as many message IDs as possible
ByteArrayOutputStream out = new ByteArrayOutputStream(length); ByteArrayOutputStream out = new ByteArrayOutputStream(length);
ProtocolWriter writer = protocolWriterFactory.createProtocolWriter(out, ProtocolWriter writer = protocolWriterFactory.createProtocolWriter(out,
@@ -170,7 +160,7 @@ public class ConstantsTest extends BriarTestCase {
} }
// Add the transports to an update // Add the transports to an update
ByteArrayOutputStream out = ByteArrayOutputStream out =
new ByteArrayOutputStream(MAX_PACKET_LENGTH); new ByteArrayOutputStream(MAX_PACKET_LENGTH);
ProtocolWriter writer = protocolWriterFactory.createProtocolWriter(out, ProtocolWriter writer = protocolWriterFactory.createProtocolWriter(out,
true); true);
TransportUpdate t = packetFactory.createTransportUpdate(transports, TransportUpdate t = packetFactory.createTransportUpdate(transports,

View File

@@ -1,134 +0,0 @@
package net.sf.briar.protocol;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.BitSet;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import net.sf.briar.BriarTestCase;
import net.sf.briar.TestUtils;
import net.sf.briar.api.protocol.Ack;
import net.sf.briar.api.protocol.Batch;
import net.sf.briar.api.protocol.BatchId;
import net.sf.briar.api.protocol.Group;
import net.sf.briar.api.protocol.GroupFactory;
import net.sf.briar.api.protocol.GroupId;
import net.sf.briar.api.protocol.Message;
import net.sf.briar.api.protocol.MessageFactory;
import net.sf.briar.api.protocol.Offer;
import net.sf.briar.api.protocol.PacketFactory;
import net.sf.briar.api.protocol.ProtocolReader;
import net.sf.briar.api.protocol.ProtocolReaderFactory;
import net.sf.briar.api.protocol.ProtocolWriter;
import net.sf.briar.api.protocol.ProtocolWriterFactory;
import net.sf.briar.api.protocol.RawBatch;
import net.sf.briar.api.protocol.Request;
import net.sf.briar.api.protocol.SubscriptionUpdate;
import net.sf.briar.api.protocol.Transport;
import net.sf.briar.api.protocol.TransportId;
import net.sf.briar.api.protocol.TransportUpdate;
import net.sf.briar.clock.ClockModule;
import net.sf.briar.crypto.CryptoModule;
import net.sf.briar.serial.SerialModule;
import org.junit.Test;
import com.google.inject.Guice;
import com.google.inject.Injector;
public class ProtocolIntegrationTest extends BriarTestCase {
private final ProtocolReaderFactory readerFactory;
private final ProtocolWriterFactory writerFactory;
private final PacketFactory packetFactory;
private final BatchId batchId;
private final Group group;
private final Message message;
private final String subject = "Hello";
private final String messageBody = "Hello world";
private final BitSet bitSet;
private final Map<Group, Long> subscriptions;
private final Collection<Transport> transports;
private final long timestamp = System.currentTimeMillis();
public ProtocolIntegrationTest() throws Exception {
super();
Injector i = Guice.createInjector(new ClockModule(), new CryptoModule(),
new ProtocolModule(), new SerialModule());
readerFactory = i.getInstance(ProtocolReaderFactory.class);
writerFactory = i.getInstance(ProtocolWriterFactory.class);
packetFactory = i.getInstance(PacketFactory.class);
batchId = new BatchId(TestUtils.getRandomId());
GroupFactory groupFactory = i.getInstance(GroupFactory.class);
group = groupFactory.createGroup("Unrestricted group", null);
MessageFactory messageFactory = i.getInstance(MessageFactory.class);
message = messageFactory.createMessage(null, group, subject,
messageBody.getBytes("UTF-8"));
bitSet = new BitSet();
bitSet.set(3);
bitSet.set(7);
subscriptions = Collections.singletonMap(group, 123L);
TransportId transportId = new TransportId(TestUtils.getRandomId());
Transport transport = new Transport(transportId,
Collections.singletonMap("bar", "baz"));
transports = Collections.singletonList(transport);
}
@Test
public void testWriteAndRead() throws Exception {
// Write
ByteArrayOutputStream out = new ByteArrayOutputStream();
ProtocolWriter writer = writerFactory.createProtocolWriter(out, true);
Ack a = packetFactory.createAck(Collections.singletonList(batchId));
writer.writeAck(a);
RawBatch b = packetFactory.createBatch(Collections.singletonList(
message.getSerialised()));
writer.writeBatch(b);
Offer o = packetFactory.createOffer(Collections.singletonList(
message.getId()));
writer.writeOffer(o);
Request r = packetFactory.createRequest(bitSet, 10);
writer.writeRequest(r);
SubscriptionUpdate s = packetFactory.createSubscriptionUpdate(
Collections.<GroupId, GroupId>emptyMap(), subscriptions, 0L,
timestamp);
writer.writeSubscriptionUpdate(s);
TransportUpdate t = packetFactory.createTransportUpdate(transports,
timestamp);
writer.writeTransportUpdate(t);
// Read
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
ProtocolReader reader = readerFactory.createProtocolReader(in);
a = reader.readAck();
assertEquals(Collections.singletonList(batchId), a.getBatchIds());
Batch b1 = reader.readBatch().verify();
assertEquals(Collections.singletonList(message), b1.getMessages());
o = reader.readOffer();
assertEquals(Collections.singletonList(message.getId()),
o.getMessageIds());
r = reader.readRequest();
assertEquals(bitSet, r.getBitmap());
assertEquals(10, r.getLength());
s = reader.readSubscriptionUpdate();
assertEquals(subscriptions, s.getSubscriptions());
assertEquals(timestamp, s.getTimestamp());
t = reader.readTransportUpdate();
assertEquals(transports, t.getTransports());
assertEquals(timestamp, t.getTimestamp());
}
}

View File

@@ -1,242 +0,0 @@
package net.sf.briar.protocol;
import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.security.Signature;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.Random;
import net.sf.briar.BriarTestCase;
import net.sf.briar.TestUtils;
import net.sf.briar.api.crypto.CryptoComponent;
import net.sf.briar.api.crypto.MessageDigest;
import net.sf.briar.api.protocol.Author;
import net.sf.briar.api.protocol.AuthorId;
import net.sf.briar.api.protocol.Batch;
import net.sf.briar.api.protocol.BatchId;
import net.sf.briar.api.protocol.Group;
import net.sf.briar.api.protocol.GroupId;
import net.sf.briar.api.protocol.Message;
import net.sf.briar.api.protocol.MessageId;
import net.sf.briar.api.protocol.UnverifiedBatch;
import net.sf.briar.crypto.CryptoModule;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.junit.Test;
import com.google.inject.Guice;
import com.google.inject.Injector;
public class UnverifiedBatchImplTest extends BriarTestCase {
// FIXME: This is an integration test, not a unit test
private final CryptoComponent crypto;
private final byte[] raw, raw1;
private final String subject;
private final long timestamp;
public UnverifiedBatchImplTest() {
super();
Injector i = Guice.createInjector(new CryptoModule());
crypto = i.getInstance(CryptoComponent.class);
Random r = new Random();
raw = new byte[123];
r.nextBytes(raw);
raw1 = new byte[1234];
r.nextBytes(raw1);
subject = "Unit tests are exciting";
timestamp = System.currentTimeMillis();
}
@Test
public void testIds() throws Exception {
// Calculate the expected batch and message IDs
MessageDigest messageDigest = crypto.getMessageDigest();
messageDigest.update(raw);
messageDigest.update(raw1);
BatchId batchId = new BatchId(messageDigest.digest());
messageDigest.update(raw);
MessageId messageId = new MessageId(messageDigest.digest());
messageDigest.update(raw1);
MessageId messageId1 = new MessageId(messageDigest.digest());
// Verify the batch
Mockery context = new Mockery();
final UnverifiedMessage message =
context.mock(UnverifiedMessage.class, "message");
final UnverifiedMessage message1 =
context.mock(UnverifiedMessage.class, "message1");
context.checking(new Expectations() {{
// First message
oneOf(message).getRaw();
will(returnValue(raw));
oneOf(message).getAuthor();
will(returnValue(null));
oneOf(message).getGroup();
will(returnValue(null));
oneOf(message).getParent();
will(returnValue(null));
oneOf(message).getSubject();
will(returnValue(subject));
oneOf(message).getTimestamp();
will(returnValue(timestamp));
oneOf(message).getBodyStart();
will(returnValue(10));
oneOf(message).getBodyLength();
will(returnValue(100));
// Second message
oneOf(message1).getRaw();
will(returnValue(raw1));
oneOf(message1).getAuthor();
will(returnValue(null));
oneOf(message1).getGroup();
will(returnValue(null));
oneOf(message1).getParent();
will(returnValue(null));
oneOf(message1).getSubject();
will(returnValue(subject));
oneOf(message1).getTimestamp();
will(returnValue(timestamp));
oneOf(message1).getBodyStart();
will(returnValue(10));
oneOf(message1).getBodyLength();
will(returnValue(1000));
}});
Collection<UnverifiedMessage> messages = Arrays.asList(message,
message1);
UnverifiedBatch batch = new UnverifiedBatchImpl(crypto, messages);
Batch verifiedBatch = batch.verify();
// Check that the batch and message IDs match
assertEquals(batchId, verifiedBatch.getId());
Collection<Message> verifiedMessages = verifiedBatch.getMessages();
assertEquals(2, verifiedMessages.size());
Iterator<Message> it = verifiedMessages.iterator();
Message verifiedMessage = it.next();
assertEquals(messageId, verifiedMessage.getId());
Message verifiedMessage1 = it.next();
assertEquals(messageId1, verifiedMessage1.getId());
context.assertIsSatisfied();
}
@Test
public void testSignatures() throws Exception {
final int signedByAuthor = 100, signedByGroup = 110;
final KeyPair authorKeyPair = crypto.generateSignatureKeyPair();
final KeyPair groupKeyPair = crypto.generateSignatureKeyPair();
GroupId groupId = new GroupId(TestUtils.getRandomId());
final Group group = new Group(groupId, "Group name",
groupKeyPair.getPublic().getEncoded());
Signature signature = crypto.getSignature();
// Calculate the expected author and group signatures
signature.initSign(authorKeyPair.getPrivate());
signature.update(raw, 0, signedByAuthor);
final byte[] authorSignature = signature.sign();
signature.initSign(groupKeyPair.getPrivate());
signature.update(raw, 0, signedByGroup);
final byte[] groupSignature = signature.sign();
// Verify the batch
Mockery context = new Mockery();
final UnverifiedMessage message =
context.mock(UnverifiedMessage.class, "message");
final Author author = context.mock(Author.class);
final UnverifiedMessage message1 =
context.mock(UnverifiedMessage.class, "message1");
context.checking(new Expectations() {{
// First message
oneOf(message).getRaw();
will(returnValue(raw));
oneOf(message).getAuthor();
will(returnValue(author));
oneOf(author).getPublicKey();
will(returnValue(authorKeyPair.getPublic().getEncoded()));
oneOf(message).getLengthSignedByAuthor();
will(returnValue(signedByAuthor));
oneOf(message).getAuthorSignature();
will(returnValue(authorSignature));
oneOf(message).getGroup();
will(returnValue(group));
oneOf(message).getLengthSignedByGroup();
will(returnValue(signedByGroup));
oneOf(message).getGroupSignature();
will(returnValue(groupSignature));
oneOf(author).getId();
will(returnValue(new AuthorId(TestUtils.getRandomId())));
oneOf(message).getParent();
will(returnValue(null));
oneOf(message).getSubject();
will(returnValue(subject));
oneOf(message).getTimestamp();
will(returnValue(timestamp));
oneOf(message).getBodyStart();
will(returnValue(10));
oneOf(message).getBodyLength();
will(returnValue(100));
// Second message
oneOf(message1).getRaw();
will(returnValue(raw1));
oneOf(message1).getAuthor();
will(returnValue(null));
oneOf(message1).getGroup();
will(returnValue(null));
oneOf(message1).getParent();
will(returnValue(null));
oneOf(message1).getSubject();
will(returnValue(subject));
oneOf(message1).getTimestamp();
will(returnValue(timestamp));
oneOf(message1).getBodyStart();
will(returnValue(10));
oneOf(message1).getBodyLength();
will(returnValue(1000));
}});
Collection<UnverifiedMessage> messages = Arrays.asList(message,
message1);
UnverifiedBatch batch = new UnverifiedBatchImpl(crypto, messages);
batch.verify();
context.assertIsSatisfied();
}
@Test
public void testExceptionThrownIfMessageIsModified() throws Exception {
final int signedByAuthor = 100;
final KeyPair authorKeyPair = crypto.generateSignatureKeyPair();
Signature signature = crypto.getSignature();
// Calculate the expected author signature
signature.initSign(authorKeyPair.getPrivate());
signature.update(raw, 0, signedByAuthor);
final byte[] authorSignature = signature.sign();
// Modify the message
raw[signedByAuthor / 2] ^= 0xff;
// Verify the batch
Mockery context = new Mockery();
final UnverifiedMessage message =
context.mock(UnverifiedMessage.class, "message");
final Author author = context.mock(Author.class);
final UnverifiedMessage message1 =
context.mock(UnverifiedMessage.class, "message1");
context.checking(new Expectations() {{
// First message - verification will fail at the author's signature
oneOf(message).getRaw();
will(returnValue(raw));
oneOf(message).getAuthor();
will(returnValue(author));
oneOf(author).getPublicKey();
will(returnValue(authorKeyPair.getPublic().getEncoded()));
oneOf(message).getLengthSignedByAuthor();
will(returnValue(signedByAuthor));
oneOf(message).getAuthorSignature();
will(returnValue(authorSignature));
}});
Collection<UnverifiedMessage> messages = Arrays.asList(message,
message1);
UnverifiedBatch batch = new UnverifiedBatchImpl(crypto, messages);
try {
batch.verify();
fail();
} catch(GeneralSecurityException expected) {}
context.assertIsSatisfied();
}
}

View File

@@ -17,9 +17,8 @@ import net.sf.briar.api.ContactId;
import net.sf.briar.api.db.DatabaseComponent; import net.sf.briar.api.db.DatabaseComponent;
import net.sf.briar.api.db.DatabaseExecutor; import net.sf.briar.api.db.DatabaseExecutor;
import net.sf.briar.api.protocol.Ack; import net.sf.briar.api.protocol.Ack;
import net.sf.briar.api.protocol.BatchId; import net.sf.briar.api.protocol.MessageId;
import net.sf.briar.api.protocol.ProtocolWriterFactory; 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.TransportId;
import net.sf.briar.api.protocol.UniqueId; import net.sf.briar.api.protocol.UniqueId;
import net.sf.briar.api.transport.ConnectionContext; import net.sf.briar.api.transport.ConnectionContext;
@@ -51,6 +50,7 @@ public class OutgoingSimplexConnectionTest extends BriarTestCase {
private final ConnectionWriterFactory connFactory; private final ConnectionWriterFactory connFactory;
private final ProtocolWriterFactory protoFactory; private final ProtocolWriterFactory protoFactory;
private final ContactId contactId; private final ContactId contactId;
private final MessageId messageId;
private final TransportId transportId; private final TransportId transportId;
private final byte[] secret; private final byte[] secret;
@@ -75,6 +75,7 @@ public class OutgoingSimplexConnectionTest extends BriarTestCase {
connFactory = i.getInstance(ConnectionWriterFactory.class); connFactory = i.getInstance(ConnectionWriterFactory.class);
protoFactory = i.getInstance(ProtocolWriterFactory.class); protoFactory = i.getInstance(ProtocolWriterFactory.class);
contactId = new ContactId(234); contactId = new ContactId(234);
messageId = new MessageId(TestUtils.getRandomId());
transportId = new TransportId(TestUtils.getRandomId()); transportId = new TransportId(TestUtils.getRandomId());
secret = new byte[32]; secret = new byte[32];
} }
@@ -115,7 +116,7 @@ public class OutgoingSimplexConnectionTest extends BriarTestCase {
// No acks to send // No acks to send
oneOf(db).generateAck(with(contactId), with(any(int.class))); oneOf(db).generateAck(with(contactId), with(any(int.class)));
will(returnValue(null)); will(returnValue(null));
// No batches to send // No messages to send
oneOf(db).generateBatch(with(contactId), with(any(int.class))); oneOf(db).generateBatch(with(contactId), with(any(int.class)));
will(returnValue(null)); will(returnValue(null));
}}); }});
@@ -138,9 +139,7 @@ public class OutgoingSimplexConnectionTest extends BriarTestCase {
OutgoingSimplexConnection connection = new OutgoingSimplexConnection(db, OutgoingSimplexConnection connection = new OutgoingSimplexConnection(db,
connRegistry, connFactory, protoFactory, ctx, transport); connRegistry, connFactory, protoFactory, ctx, transport);
final Ack ack = context.mock(Ack.class); final Ack ack = context.mock(Ack.class);
final BatchId batchId = new BatchId(TestUtils.getRandomId()); final byte[] raw = new byte[1234];
final RawBatch batch = context.mock(RawBatch.class);
final byte[] message = new byte[1234];
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// No transports to send // No transports to send
oneOf(db).generateTransportUpdate(contactId); oneOf(db).generateTransportUpdate(contactId);
@@ -151,24 +150,22 @@ public class OutgoingSimplexConnectionTest extends BriarTestCase {
// One ack to send // One ack to send
oneOf(db).generateAck(with(contactId), with(any(int.class))); oneOf(db).generateAck(with(contactId), with(any(int.class)));
will(returnValue(ack)); will(returnValue(ack));
oneOf(ack).getBatchIds(); oneOf(ack).getMessageIds();
will(returnValue(Collections.singletonList(batchId))); will(returnValue(Collections.singletonList(messageId)));
// No more acks // No more acks
oneOf(db).generateAck(with(contactId), with(any(int.class))); oneOf(db).generateAck(with(contactId), with(any(int.class)));
will(returnValue(null)); will(returnValue(null));
// One batch to send // One message to send
oneOf(db).generateBatch(with(contactId), with(any(int.class))); oneOf(db).generateBatch(with(contactId), with(any(int.class)));
will(returnValue(batch)); will(returnValue(Collections.singletonList(raw)));
oneOf(batch).getMessages(); // No more messages
will(returnValue(Collections.singletonList(message)));
// No more batches
oneOf(db).generateBatch(with(contactId), with(any(int.class))); oneOf(db).generateBatch(with(contactId), with(any(int.class)));
will(returnValue(null)); will(returnValue(null));
}}); }});
connection.write(); connection.write();
// Something should have been written // Something should have been written
int overhead = TAG_LENGTH + HEADER_LENGTH + MAC_LENGTH; int overhead = TAG_LENGTH + HEADER_LENGTH + MAC_LENGTH;
assertTrue(out.size() > overhead + UniqueId.LENGTH + message.length); assertTrue(out.size() > overhead + UniqueId.LENGTH + raw.length);
// The transport should have been disposed with exception == false // The transport should have been disposed with exception == false
assertTrue(transport.getDisposed()); assertTrue(transport.getDisposed());
assertFalse(transport.getException()); assertFalse(transport.getException());

View File

@@ -17,9 +17,10 @@ import net.sf.briar.api.crypto.KeyManager;
import net.sf.briar.api.db.DatabaseComponent; import net.sf.briar.api.db.DatabaseComponent;
import net.sf.briar.api.db.event.DatabaseEvent; import net.sf.briar.api.db.event.DatabaseEvent;
import net.sf.briar.api.db.event.DatabaseListener; import net.sf.briar.api.db.event.DatabaseListener;
import net.sf.briar.api.db.event.MessagesAddedEvent; import net.sf.briar.api.db.event.MessageAddedEvent;
import net.sf.briar.api.protocol.Message; import net.sf.briar.api.protocol.Message;
import net.sf.briar.api.protocol.MessageFactory; import net.sf.briar.api.protocol.MessageFactory;
import net.sf.briar.api.protocol.MessageVerifier;
import net.sf.briar.api.protocol.ProtocolReaderFactory; import net.sf.briar.api.protocol.ProtocolReaderFactory;
import net.sf.briar.api.protocol.ProtocolWriterFactory; import net.sf.briar.api.protocol.ProtocolWriterFactory;
import net.sf.briar.api.protocol.Transport; import net.sf.briar.api.protocol.Transport;
@@ -181,6 +182,8 @@ public class SimplexProtocolIntegrationTest extends BriarTestCase {
ConnectionContext ctx = rec.acceptConnection(transportId, tag); ConnectionContext ctx = rec.acceptConnection(transportId, tag);
assertNotNull(ctx); assertNotNull(ctx);
// Create an incoming simplex connection // Create an incoming simplex connection
MessageVerifier messageVerifier =
bob.getInstance(MessageVerifier.class);
ConnectionRegistry connRegistry = ConnectionRegistry connRegistry =
bob.getInstance(ConnectionRegistry.class); bob.getInstance(ConnectionRegistry.class);
ConnectionReaderFactory connFactory = ConnectionReaderFactory connFactory =
@@ -190,8 +193,9 @@ public class SimplexProtocolIntegrationTest extends BriarTestCase {
TestSimplexTransportReader transport = TestSimplexTransportReader transport =
new TestSimplexTransportReader(in); new TestSimplexTransportReader(in);
IncomingSimplexConnection simplex = new IncomingSimplexConnection( IncomingSimplexConnection simplex = new IncomingSimplexConnection(
new ImmediateExecutor(), new ImmediateExecutor(), db, new ImmediateExecutor(), new ImmediateExecutor(),
connRegistry, connFactory, protoFactory, ctx, transport); messageVerifier, db, connRegistry, connFactory, protoFactory,
ctx, transport);
// No messages should have been added yet // No messages should have been added yet
assertFalse(listener.messagesAdded); assertFalse(listener.messagesAdded);
// Read whatever needs to be read // Read whatever needs to be read
@@ -216,8 +220,7 @@ public class SimplexProtocolIntegrationTest extends BriarTestCase {
private boolean messagesAdded = false; private boolean messagesAdded = false;
public void eventOccurred(DatabaseEvent e) { public void eventOccurred(DatabaseEvent e) {
if(e instanceof MessagesAddedEvent) if(e instanceof MessageAddedEvent) messagesAdded = true;
messagesAdded = true;
} }
} }
} }

View File

@@ -8,7 +8,6 @@ import org.jmock.Expectations;
import org.jmock.Mockery; import org.jmock.Mockery;
import org.junit.Test; import org.junit.Test;
public class ConnectionWriterImplTest extends BriarTestCase { public class ConnectionWriterImplTest extends BriarTestCase {
private static final int FRAME_LENGTH = 1024; private static final int FRAME_LENGTH = 1024;

View File

@@ -18,19 +18,12 @@ import net.sf.briar.api.crypto.CryptoComponent;
import net.sf.briar.api.crypto.ErasableKey; import net.sf.briar.api.crypto.ErasableKey;
import net.sf.briar.api.protocol.TransportId; import net.sf.briar.api.protocol.TransportId;
import net.sf.briar.api.transport.ConnectionContext; import net.sf.briar.api.transport.ConnectionContext;
import net.sf.briar.api.transport.ConnectionReader;
import net.sf.briar.api.transport.ConnectionWriter; import net.sf.briar.api.transport.ConnectionWriter;
import net.sf.briar.api.transport.ConnectionWriterFactory; import net.sf.briar.api.transport.ConnectionWriterFactory;
import net.sf.briar.crypto.CryptoModule; import net.sf.briar.crypto.CryptoModule;
import net.sf.briar.transport.ConnectionReaderImpl;
import net.sf.briar.transport.ConnectionWriterFactoryImpl;
import net.sf.briar.transport.ConnectionWriterImpl;
import net.sf.briar.transport.IncomingEncryptionLayer;
import net.sf.briar.transport.OutgoingEncryptionLayer;
import org.junit.Test; import org.junit.Test;
import com.google.inject.AbstractModule; import com.google.inject.AbstractModule;
import com.google.inject.Guice; import com.google.inject.Guice;
import com.google.inject.Injector; import com.google.inject.Injector;
@@ -93,7 +86,7 @@ public class TransportIntegrationTest extends BriarTestCase {
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
FrameWriter encryptionOut = new OutgoingEncryptionLayer(out, FrameWriter encryptionOut = new OutgoingEncryptionLayer(out,
Long.MAX_VALUE, frameCipher, frameCopy, FRAME_LENGTH); Long.MAX_VALUE, frameCipher, frameCopy, FRAME_LENGTH);
ConnectionWriter writer = new ConnectionWriterImpl(encryptionOut, ConnectionWriterImpl writer = new ConnectionWriterImpl(encryptionOut,
FRAME_LENGTH); FRAME_LENGTH);
OutputStream out1 = writer.getOutputStream(); OutputStream out1 = writer.getOutputStream();
out1.write(frame); out1.write(frame);
@@ -106,7 +99,7 @@ public class TransportIntegrationTest extends BriarTestCase {
ByteArrayInputStream in = new ByteArrayInputStream(output); ByteArrayInputStream in = new ByteArrayInputStream(output);
FrameReader encryptionIn = new IncomingEncryptionLayer(in, frameCipher, FrameReader encryptionIn = new IncomingEncryptionLayer(in, frameCipher,
frameKey, FRAME_LENGTH); frameKey, FRAME_LENGTH);
ConnectionReader reader = new ConnectionReaderImpl(encryptionIn, ConnectionReaderImpl reader = new ConnectionReaderImpl(encryptionIn,
FRAME_LENGTH); FRAME_LENGTH);
InputStream in1 = reader.getInputStream(); InputStream in1 = reader.getInputStream();
byte[] recovered = new byte[frame.length]; byte[] recovered = new byte[frame.length];