mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-13 03:09:04 +01:00
Removed batches from BMP. Messages are now sent and acked individually.
This commit is contained in:
@@ -11,13 +11,11 @@ import net.sf.briar.api.TransportProperties;
|
||||
import net.sf.briar.api.db.event.DatabaseListener;
|
||||
import net.sf.briar.api.protocol.Ack;
|
||||
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.GroupId;
|
||||
import net.sf.briar.api.protocol.Message;
|
||||
import net.sf.briar.api.protocol.MessageId;
|
||||
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.SubscriptionUpdate;
|
||||
import net.sf.briar.api.protocol.Transport;
|
||||
@@ -70,25 +68,28 @@ public interface DatabaseComponent {
|
||||
|
||||
/**
|
||||
* 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
|
||||
* there are no sendable messages that fit in the given capacity.
|
||||
* Generates a batch of raw messages for the given contact, with a total
|
||||
* 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
|
||||
* collection of requested messages. Any messages that were either added to
|
||||
* the batch, or were considered but are no longer sendable to the contact,
|
||||
* are removed from the collection of requested messages before returning.
|
||||
* Generates a batch of raw messages for the given contact from the given
|
||||
* collection of requested messages, with a total length less than or equal
|
||||
* to the given length. Any messages that were either added to the batch,
|
||||
* 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
|
||||
* capacity.
|
||||
* length.
|
||||
*/
|
||||
RawBatch generateBatch(ContactId c, int capacity,
|
||||
Collection<byte[]> generateBatch(ContactId c, int maxLength,
|
||||
Collection<MessageId> requested) throws DbException;
|
||||
|
||||
/**
|
||||
@@ -170,8 +171,8 @@ public interface DatabaseComponent {
|
||||
/** Processes an acknowledgement from the given contact. */
|
||||
void receiveAck(ContactId c, Ack a) throws DbException;
|
||||
|
||||
/** Processes a batch of messages from the given contact. */
|
||||
void receiveBatch(ContactId c, Batch b) throws DbException;
|
||||
/** Processes a message from the given contact. */
|
||||
void receiveMessage(ContactId c, Message m) throws DbException;
|
||||
|
||||
/**
|
||||
* Processes an offer from the given contact and generates a request for
|
||||
|
||||
@@ -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 {
|
||||
|
||||
}
|
||||
@@ -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
|
||||
* database.
|
||||
*/
|
||||
public class MessagesAddedEvent extends DatabaseEvent {
|
||||
public class MessageAddedEvent extends DatabaseEvent {
|
||||
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
}
|
||||
@@ -2,9 +2,9 @@ package net.sf.briar.api.protocol;
|
||||
|
||||
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 {
|
||||
|
||||
/** Returns the IDs of the acknowledged batches. */
|
||||
Collection<BatchId> getBatchIds();
|
||||
/** Returns the IDs of the acknowledged messages. */
|
||||
Collection<MessageId> getMessageIds();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package net.sf.briar.api.protocol;
|
||||
|
||||
import java.security.GeneralSecurityException;
|
||||
|
||||
public interface MessageVerifier {
|
||||
|
||||
Message verifyMessage(UnverifiedMessage m) throws GeneralSecurityException;
|
||||
}
|
||||
@@ -6,9 +6,7 @@ import java.util.Map;
|
||||
|
||||
public interface PacketFactory {
|
||||
|
||||
Ack createAck(Collection<BatchId> acked);
|
||||
|
||||
RawBatch createBatch(Collection<byte[]> messages);
|
||||
Ack createAck(Collection<MessageId> acked);
|
||||
|
||||
Offer createOffer(Collection<MessageId> offered);
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@ public interface ProtocolReader {
|
||||
boolean hasAck() throws IOException;
|
||||
Ack readAck() throws IOException;
|
||||
|
||||
boolean hasBatch() throws IOException;
|
||||
UnverifiedBatch readBatch() throws IOException;
|
||||
boolean hasMessage() throws IOException;
|
||||
UnverifiedMessage readMessage() throws IOException;
|
||||
|
||||
boolean hasOffer() throws IOException;
|
||||
Offer readOffer() throws IOException;
|
||||
|
||||
@@ -4,15 +4,13 @@ import java.io.IOException;
|
||||
|
||||
public interface ProtocolWriter {
|
||||
|
||||
int getMaxBatchesForAck(long capacity);
|
||||
int getMaxMessagesForAck(long capacity);
|
||||
|
||||
int getMaxMessagesForOffer(long capacity);
|
||||
|
||||
int getMessageCapacityForBatch(long capacity);
|
||||
|
||||
void writeAck(Ack a) throws IOException;
|
||||
|
||||
void writeBatch(RawBatch b) throws IOException;
|
||||
void writeMessage(byte[] raw) throws IOException;
|
||||
|
||||
void writeOffer(Offer o) throws IOException;
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -5,7 +5,6 @@ public interface Types {
|
||||
|
||||
int ACK = 0;
|
||||
int AUTHOR = 1;
|
||||
int BATCH = 2;
|
||||
int GROUP = 3;
|
||||
int MESSAGE = 4;
|
||||
int OFFER = 5;
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
package net.sf.briar.api.protocol;
|
||||
|
||||
import java.security.GeneralSecurityException;
|
||||
|
||||
public interface UnverifiedBatch {
|
||||
|
||||
Batch verify() throws GeneralSecurityException;
|
||||
}
|
||||
@@ -1,10 +1,6 @@
|
||||
package net.sf.briar.protocol;
|
||||
package net.sf.briar.api.protocol;
|
||||
|
||||
import net.sf.briar.api.protocol.Author;
|
||||
import net.sf.briar.api.protocol.Group;
|
||||
import net.sf.briar.api.protocol.MessageId;
|
||||
|
||||
interface UnverifiedMessage {
|
||||
public interface UnverifiedMessage {
|
||||
|
||||
MessageId getParent();
|
||||
|
||||
@@ -16,7 +12,7 @@ interface UnverifiedMessage {
|
||||
|
||||
long getTimestamp();
|
||||
|
||||
byte[] getRaw();
|
||||
byte[] getSerialised();
|
||||
|
||||
byte[] getAuthorSignature();
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
<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-gpl-2.1.1-SNAPSHOT.jar"/>
|
||||
<classpathentry kind="lib" path="libs/javax.inject.jar"/>
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/briar-api"/>
|
||||
<classpathentry kind="lib" path="/briar-api/libs/android.jar"/>
|
||||
<classpathentry kind="lib" path="/briar-api/libs/guice-3.0-no_aop.jar"/>
|
||||
|
||||
@@ -11,7 +11,6 @@ import net.sf.briar.api.TransportProperties;
|
||||
import net.sf.briar.api.db.DbException;
|
||||
import net.sf.briar.api.db.MessageHeader;
|
||||
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.GroupId;
|
||||
import net.sf.briar.api.protocol.Message;
|
||||
@@ -71,13 +70,6 @@ interface Database<T> {
|
||||
*/
|
||||
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.
|
||||
* <p>
|
||||
@@ -101,12 +93,19 @@ interface Database<T> {
|
||||
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>
|
||||
* Locking: contact read, message read, messageStatus write.
|
||||
*/
|
||||
void addOutstandingBatch(T txn, ContactId c, BatchId b,
|
||||
Collection<MessageId> sent) throws DbException;
|
||||
void addOutstandingMessages(T txn, ContactId c, Collection<MessageId> sent)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* 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,
|
||||
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.
|
||||
* <p>
|
||||
@@ -267,12 +257,13 @@ interface Database<T> {
|
||||
Collection<Transport> getLocalTransports(T txn) throws DbException;
|
||||
|
||||
/**
|
||||
* Returns the IDs of any batches sent to the given contact that should now
|
||||
* be considered lost.
|
||||
* Returns the IDs of any messages sent to the given contact that should
|
||||
* now be considered lost.
|
||||
* <p>
|
||||
* 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.
|
||||
@@ -315,6 +306,15 @@ interface Database<T> {
|
||||
Collection<MessageId> getMessagesByAuthor(T txn, AuthorId a)
|
||||
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
|
||||
* 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
|
||||
* 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>
|
||||
* Locking: contact read, message read, messageStatus read,
|
||||
* subscription read.
|
||||
*/
|
||||
Collection<MessageId> getSendableMessages(T txn, ContactId c, int capacity)
|
||||
Collection<MessageId> getSendableMessages(T txn, ContactId c, int maxLength)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
@@ -493,21 +494,22 @@ interface Database<T> {
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Removes an outstanding batch that has been acknowledged. Any messages in
|
||||
* the batch that are still considered outstanding (Status.SENT) with
|
||||
* Removes outstanding messages that have been acknowledged. Any of the
|
||||
* messages that are still considered outstanding (Status.SENT) with
|
||||
* respect to the given contact are now considered seen (Status.SEEN).
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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;
|
||||
|
||||
/**
|
||||
@@ -519,13 +521,14 @@ interface Database<T> {
|
||||
void removeContact(T txn, ContactId c) throws DbException;
|
||||
|
||||
/**
|
||||
* Removes an outstanding batch that has been lost. Any messages in the
|
||||
* batch that are still considered outstanding (Status.SENT) with respect
|
||||
* to the given contact are now considered unsent (Status.NEW).
|
||||
* Removes outstanding messages that have been lost. Any messages that are
|
||||
* still considered outstanding (Status.SENT) with respect to the given
|
||||
* contact are now considered unsent (Status.NEW).
|
||||
* <p>
|
||||
* 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.
|
||||
|
||||
@@ -32,28 +32,25 @@ import net.sf.briar.api.db.DbException;
|
||||
import net.sf.briar.api.db.MessageHeader;
|
||||
import net.sf.briar.api.db.NoSuchContactException;
|
||||
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.ContactRemovedEvent;
|
||||
import net.sf.briar.api.db.event.DatabaseEvent;
|
||||
import net.sf.briar.api.db.event.DatabaseListener;
|
||||
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.RemoteTransportsUpdatedEvent;
|
||||
import net.sf.briar.api.db.event.SubscriptionsUpdatedEvent;
|
||||
import net.sf.briar.api.lifecycle.ShutdownManager;
|
||||
import net.sf.briar.api.protocol.Ack;
|
||||
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.Offer;
|
||||
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.SubscriptionUpdate;
|
||||
import net.sf.briar.api.protocol.Transport;
|
||||
@@ -270,7 +267,7 @@ DatabaseCleaner.Callback {
|
||||
contactLock.readLock().unlock();
|
||||
}
|
||||
// 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();
|
||||
}
|
||||
// Call the listeners outside the lock
|
||||
if(added) callListeners(new MessagesAddedEvent());
|
||||
if(added) callListeners(new MessageAddedEvent());
|
||||
}
|
||||
|
||||
public void addSecrets(Collection<TemporarySecret> secrets)
|
||||
@@ -444,8 +441,8 @@ DatabaseCleaner.Callback {
|
||||
return true;
|
||||
}
|
||||
|
||||
public Ack generateAck(ContactId c, int maxBatches) throws DbException {
|
||||
Collection<BatchId> acked;
|
||||
public Ack generateAck(ContactId c, int maxMessages) throws DbException {
|
||||
Collection<MessageId> acked;
|
||||
contactLock.readLock().lock();
|
||||
try {
|
||||
messageStatusLock.readLock().lock();
|
||||
@@ -454,7 +451,7 @@ DatabaseCleaner.Callback {
|
||||
try {
|
||||
if(!db.containsContact(txn, c))
|
||||
throw new NoSuchContactException();
|
||||
acked = db.getBatchesToAck(txn, c, maxBatches);
|
||||
acked = db.getMessagesToAck(txn, c, maxMessages);
|
||||
db.commitTransaction(txn);
|
||||
} catch(DbException e) {
|
||||
db.abortTransaction(txn);
|
||||
@@ -469,7 +466,7 @@ DatabaseCleaner.Callback {
|
||||
try {
|
||||
T txn = db.startTransaction();
|
||||
try {
|
||||
db.removeBatchesToAck(txn, c, acked);
|
||||
db.removeMessagesToAck(txn, c, acked);
|
||||
db.commitTransaction(txn);
|
||||
} catch(DbException e) {
|
||||
db.abortTransaction(txn);
|
||||
@@ -484,11 +481,10 @@ DatabaseCleaner.Callback {
|
||||
return packetFactory.createAck(acked);
|
||||
}
|
||||
|
||||
public RawBatch generateBatch(ContactId c, int capacity)
|
||||
public Collection<byte[]> generateBatch(ContactId c, int maxLength)
|
||||
throws DbException {
|
||||
Collection<MessageId> ids;
|
||||
List<byte[]> messages = new ArrayList<byte[]>();
|
||||
RawBatch b;
|
||||
// Get some sendable messages from the database
|
||||
contactLock.readLock().lock();
|
||||
try {
|
||||
@@ -502,7 +498,7 @@ DatabaseCleaner.Callback {
|
||||
try {
|
||||
if(!db.containsContact(txn, c))
|
||||
throw new NoSuchContactException();
|
||||
ids = db.getSendableMessages(txn, c, capacity);
|
||||
ids = db.getSendableMessages(txn, c, maxLength);
|
||||
for(MessageId m : ids) {
|
||||
messages.add(db.getMessage(txn, m));
|
||||
}
|
||||
@@ -518,13 +514,11 @@ DatabaseCleaner.Callback {
|
||||
messageStatusLock.readLock().unlock();
|
||||
}
|
||||
if(messages.isEmpty()) return null;
|
||||
messages = Collections.unmodifiableList(messages);
|
||||
b = packetFactory.createBatch(messages);
|
||||
messageStatusLock.writeLock().lock();
|
||||
try {
|
||||
T txn = db.startTransaction();
|
||||
try {
|
||||
db.addOutstandingBatch(txn, c, b.getId(), ids);
|
||||
db.addOutstandingMessages(txn, c, ids);
|
||||
db.commitTransaction(txn);
|
||||
} catch(DbException e) {
|
||||
db.abortTransaction(txn);
|
||||
@@ -539,14 +533,13 @@ DatabaseCleaner.Callback {
|
||||
} finally {
|
||||
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> ids = new ArrayList<MessageId>();
|
||||
List<byte[]> messages = new ArrayList<byte[]>();
|
||||
RawBatch b;
|
||||
// Get some sendable messages from the database
|
||||
contactLock.readLock().lock();
|
||||
try {
|
||||
@@ -565,10 +558,10 @@ DatabaseCleaner.Callback {
|
||||
MessageId m = it.next();
|
||||
byte[] raw = db.getMessageIfSendable(txn, c, m);
|
||||
if(raw != null) {
|
||||
if(raw.length > capacity) break;
|
||||
if(raw.length > maxLength) break;
|
||||
messages.add(raw);
|
||||
ids.add(m);
|
||||
capacity -= raw.length;
|
||||
maxLength -= raw.length;
|
||||
}
|
||||
it.remove();
|
||||
}
|
||||
@@ -584,13 +577,11 @@ DatabaseCleaner.Callback {
|
||||
messageStatusLock.readLock().unlock();
|
||||
}
|
||||
if(messages.isEmpty()) return null;
|
||||
messages = Collections.unmodifiableList(messages);
|
||||
b = packetFactory.createBatch(messages);
|
||||
messageStatusLock.writeLock().lock();
|
||||
try {
|
||||
T txn = db.startTransaction();
|
||||
try {
|
||||
db.addOutstandingBatch(txn, c, b.getId(), ids);
|
||||
db.addOutstandingMessages(txn, c, ids);
|
||||
db.commitTransaction(txn);
|
||||
} catch(DbException e) {
|
||||
db.abortTransaction(txn);
|
||||
@@ -605,7 +596,7 @@ DatabaseCleaner.Callback {
|
||||
} finally {
|
||||
contactLock.readLock().unlock();
|
||||
}
|
||||
return b;
|
||||
return Collections.unmodifiableList(messages);
|
||||
}
|
||||
|
||||
public Offer generateOffer(ContactId c, int maxMessages)
|
||||
@@ -1057,12 +1048,12 @@ DatabaseCleaner.Callback {
|
||||
try {
|
||||
if(!db.containsContact(txn, c))
|
||||
throw new NoSuchContactException();
|
||||
Collection<BatchId> acks = a.getBatchIds();
|
||||
// Mark all messages in acked batches as seen
|
||||
for(BatchId b : acks) db.removeAckedBatch(txn, c, b);
|
||||
// Find any lost batches that need to be retransmitted
|
||||
Collection<BatchId> lost = db.getLostBatches(txn, c);
|
||||
for(BatchId b : lost) db.removeLostBatch(txn, c, b);
|
||||
// Mark all acked messages as seen
|
||||
db.removeAckedMessages(txn, c, a.getMessageIds());
|
||||
// Find any lost messages that need to be retransmitted
|
||||
// FIXME: Merge these methods
|
||||
Collection<MessageId> lost = db.getLostMessages(txn, c);
|
||||
if(!lost.isEmpty()) db.removeLostMessages(txn, c, lost);
|
||||
db.commitTransaction(txn);
|
||||
} catch(DbException e) {
|
||||
db.abortTransaction(txn);
|
||||
@@ -1079,8 +1070,8 @@ DatabaseCleaner.Callback {
|
||||
}
|
||||
}
|
||||
|
||||
public void receiveBatch(ContactId c, Batch b) throws DbException {
|
||||
boolean anyAdded = false;
|
||||
public void receiveMessage(ContactId c, Message m) throws DbException {
|
||||
boolean added = false;
|
||||
contactLock.readLock().lock();
|
||||
try {
|
||||
messageLock.writeLock().lock();
|
||||
@@ -1093,8 +1084,8 @@ DatabaseCleaner.Callback {
|
||||
try {
|
||||
if(!db.containsContact(txn, c))
|
||||
throw new NoSuchContactException();
|
||||
anyAdded = storeMessages(txn, c, b.getMessages());
|
||||
db.addBatchToAck(txn, c, b.getId());
|
||||
added = storeMessage(txn, c, m);
|
||||
db.addMessageToAck(txn, c, m.getId());
|
||||
db.commitTransaction(txn);
|
||||
} catch(DbException e) {
|
||||
db.abortTransaction(txn);
|
||||
@@ -1113,32 +1104,24 @@ DatabaseCleaner.Callback {
|
||||
contactLock.readLock().unlock();
|
||||
}
|
||||
// Call the listeners outside the lock
|
||||
callListeners(new BatchReceivedEvent());
|
||||
if(anyAdded) callListeners(new MessagesAddedEvent());
|
||||
callListeners(new MessageReceivedEvent());
|
||||
if(added) callListeners(new MessageAddedEvent());
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to store a collection of messages received from the given
|
||||
* contact, and returns true if any were stored.
|
||||
* Attempts to store a message received from the given contact, and returns
|
||||
* true if it was stored.
|
||||
* <p>
|
||||
* Locking: contact read, message write, messageStatus write,
|
||||
* subscription read.
|
||||
*/
|
||||
private boolean storeMessages(T txn, ContactId c,
|
||||
Collection<Message> messages) throws DbException {
|
||||
boolean anyStored = false;
|
||||
for(Message m : messages) {
|
||||
GroupId g = m.getGroup();
|
||||
if(g == null) {
|
||||
if(storePrivateMessage(txn, m, c, true)) anyStored = true;
|
||||
} else {
|
||||
long timestamp = m.getTimestamp();
|
||||
if(db.containsVisibleSubscription(txn, g, c, timestamp)) {
|
||||
if(storeGroupMessage(txn, m, c)) anyStored = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return anyStored;
|
||||
private boolean storeMessage(T txn, ContactId c, Message m)
|
||||
throws DbException {
|
||||
GroupId g = m.getGroup();
|
||||
if(g == null) return storePrivateMessage(txn, m, c, true);
|
||||
if(!db.containsVisibleSubscription(txn, g, c, m.getTimestamp()))
|
||||
return false;
|
||||
return storeGroupMessage(txn, m, c);
|
||||
}
|
||||
|
||||
public Request receiveOffer(ContactId c, Offer o) throws DbException {
|
||||
|
||||
@@ -42,12 +42,6 @@ interface DatabaseConstants {
|
||||
*/
|
||||
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
|
||||
* should be sent to a contact even if no changes have occurred.
|
||||
|
||||
@@ -3,7 +3,6 @@ package net.sf.briar.db;
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
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.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.MessageHeader;
|
||||
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.GroupId;
|
||||
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 =
|
||||
"CREATE INDEX visibilitiesByNext on visibilities (nextId)";
|
||||
|
||||
private static final String CREATE_BATCHES_TO_ACK =
|
||||
"CREATE TABLE batchesToAck"
|
||||
+ " (batchId HASH NOT NULL,"
|
||||
private static final String CREATE_MESSAGES_TO_ACK =
|
||||
"CREATE TABLE messagesToAck"
|
||||
+ " (messageId HASH NOT NULL,"
|
||||
+ " contactId INT NOT NULL,"
|
||||
+ " PRIMARY KEY (batchId, contactId),"
|
||||
+ " PRIMARY KEY (messageId, contactId),"
|
||||
+ " FOREIGN KEY (contactId) REFERENCES contacts (contactId)"
|
||||
+ " ON DELETE CASCADE)";
|
||||
|
||||
@@ -131,32 +129,6 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
+ " FOREIGN KEY (contactId) REFERENCES contacts (contactId)"
|
||||
+ " 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 =
|
||||
"CREATE TABLE ratings"
|
||||
+ " (authorId HASH NOT NULL,"
|
||||
@@ -322,11 +294,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
s.executeUpdate(insertTypeNames(CREATE_VISIBILITIES));
|
||||
s.executeUpdate(INDEX_VISIBILITIES_BY_GROUP);
|
||||
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_OUTSTANDING_BATCHES));
|
||||
s.executeUpdate(insertTypeNames(CREATE_OUTSTANDING_MESSAGES));
|
||||
s.executeUpdate(INDEX_OUTSTANDING_MESSAGES_BY_BATCH);
|
||||
s.executeUpdate(insertTypeNames(CREATE_RATINGS));
|
||||
s.executeUpdate(insertTypeNames(CREATE_STATUSES));
|
||||
s.executeUpdate(INDEX_STATUSES_BY_MESSAGE);
|
||||
@@ -452,37 +421,6 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
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 {
|
||||
PreparedStatement ps = 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 {
|
||||
PreparedStatement ps = null;
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
// Create an outstanding batch row
|
||||
String sql = "INSERT INTO outstandingBatches"
|
||||
+ " (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 = ?"
|
||||
// Set the status of each message to SENT if it's currently NEW
|
||||
String sql = "UPDATE statuses SET status = ?"
|
||||
+ " WHERE messageId = ? AND contactId = ? AND status = ?";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setShort(1, (short) Status.SENT.ordinal());
|
||||
@@ -641,7 +567,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
ps.setBytes(2, m.getBytes());
|
||||
ps.addBatch();
|
||||
}
|
||||
batchAffected = ps.executeBatch();
|
||||
int[] batchAffected = ps.executeBatch();
|
||||
if(batchAffected.length != sent.size())
|
||||
throw new DbStateException();
|
||||
for(int i = 0; i < batchAffected.length; i++) {
|
||||
@@ -649,7 +575,6 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
}
|
||||
ps.close();
|
||||
} catch(SQLException e) {
|
||||
tryToClose(rs);
|
||||
tryToClose(ps);
|
||||
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)
|
||||
throws DbException {
|
||||
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 {
|
||||
PreparedStatement ps = null;
|
||||
ResultSet rs = null;
|
||||
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);
|
||||
}
|
||||
// FIXME: Retransmission
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
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)
|
||||
throws DbException {
|
||||
PreparedStatement ps = null;
|
||||
@@ -1670,7 +1578,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
}
|
||||
|
||||
public Collection<MessageId> getSendableMessages(Connection txn,
|
||||
ContactId c, int capacity) throws DbException {
|
||||
ContactId c, int maxLength) throws DbException {
|
||||
PreparedStatement ps = null;
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
@@ -1688,13 +1596,13 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
int total = 0;
|
||||
while(rs.next()) {
|
||||
int length = rs.getInt(1);
|
||||
if(total + length > capacity) break;
|
||||
if(total + length > maxLength) break;
|
||||
ids.add(new MessageId(rs.getBytes(2)));
|
||||
total += length;
|
||||
}
|
||||
rs.close();
|
||||
ps.close();
|
||||
if(total == capacity) return Collections.unmodifiableList(ids);
|
||||
if(total == maxLength) return Collections.unmodifiableList(ids);
|
||||
// Do we have any sendable group messages?
|
||||
sql = "SELECT length, m.messageId FROM messages AS m"
|
||||
+ " JOIN contactSubscriptions AS cs"
|
||||
@@ -1719,7 +1627,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
rs = ps.executeQuery();
|
||||
while(rs.next()) {
|
||||
int length = rs.getInt(1);
|
||||
if(total + length > capacity) break;
|
||||
if(total + length > maxLength) break;
|
||||
ids.add(new MessageId(rs.getBytes(2)));
|
||||
total += length;
|
||||
}
|
||||
@@ -2067,99 +1975,52 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
}
|
||||
}
|
||||
|
||||
public void removeAckedBatch(Connection txn, ContactId c, BatchId b)
|
||||
throws DbException {
|
||||
PreparedStatement ps = null;
|
||||
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);
|
||||
public void removeAckedMessages(Connection txn, ContactId c,
|
||||
Collection<MessageId> acked) throws DbException {
|
||||
setStatus(txn, c, acked, Status.SEEN);
|
||||
}
|
||||
|
||||
private void removeBatch(Connection txn, ContactId c, BatchId b,
|
||||
Status newStatus) throws DbException {
|
||||
PreparedStatement ps = null, ps1 = null;
|
||||
ResultSet rs = null;
|
||||
private void setStatus(Connection txn, ContactId c,
|
||||
Collection<MessageId> ids, Status newStatus) throws DbException {
|
||||
PreparedStatement ps = null;
|
||||
try {
|
||||
String sql = "SELECT messageId FROM outstandingMessages"
|
||||
+ " WHERE contactId = ? AND batchId = ?";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setInt(1, c.getInt());
|
||||
ps.setBytes(2, b.getBytes());
|
||||
rs = ps.executeQuery();
|
||||
sql = "UPDATE statuses SET status = ?"
|
||||
// Set the status of each message if it's currently SENT
|
||||
String sql = "UPDATE statuses SET 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.setBytes(1, b.getBytes());
|
||||
int affected = ps.executeUpdate();
|
||||
if(affected > 1) throw new DbStateException();
|
||||
ps.close();
|
||||
} catch(SQLException e) {
|
||||
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.setShort(1, (short) newStatus.ordinal());
|
||||
ps.setInt(3, c.getInt());
|
||||
ps.setShort(4, (short) Status.SENT.ordinal());
|
||||
for(MessageId m : ids) {
|
||||
ps.setBytes(2, m.getBytes());
|
||||
ps.addBatch();
|
||||
}
|
||||
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();
|
||||
for(int i = 0; i < batchAffected.length; i++) {
|
||||
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)
|
||||
throws DbException {
|
||||
removeBatch(txn, c, b, Status.NEW);
|
||||
public void removeLostMessages(Connection txn, ContactId c,
|
||||
Collection<MessageId> lost) throws DbException {
|
||||
setStatus(txn, c, lost, Status.NEW);
|
||||
}
|
||||
|
||||
public void removeMessage(Connection txn, MessageId m) throws DbException {
|
||||
|
||||
@@ -3,17 +3,17 @@ package net.sf.briar.protocol;
|
||||
import java.util.Collection;
|
||||
|
||||
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 {
|
||||
|
||||
private final Collection<BatchId> acked;
|
||||
private final Collection<MessageId> acked;
|
||||
|
||||
AckImpl(Collection<BatchId> acked) {
|
||||
AckImpl(Collection<MessageId> acked) {
|
||||
this.acked = acked;
|
||||
}
|
||||
|
||||
public Collection<BatchId> getBatchIds() {
|
||||
public Collection<MessageId> getMessageIds() {
|
||||
return acked;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import java.util.List;
|
||||
import net.sf.briar.api.Bytes;
|
||||
import net.sf.briar.api.FormatException;
|
||||
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.Types;
|
||||
import net.sf.briar.api.protocol.UniqueId;
|
||||
@@ -38,14 +38,14 @@ class AckReader implements StructReader<Ack> {
|
||||
r.resetMaxBytesLength();
|
||||
r.removeConsumer(counting);
|
||||
if(raw.isEmpty()) throw new FormatException();
|
||||
// Convert the byte arrays to batch IDs
|
||||
List<BatchId> batches = new ArrayList<BatchId>();
|
||||
// Convert the byte arrays to message IDs
|
||||
List<MessageId> acked = new ArrayList<MessageId>();
|
||||
for(Bytes b : raw) {
|
||||
if(b.getBytes().length != UniqueId.LENGTH)
|
||||
throw new FormatException();
|
||||
batches.add(new BatchId(b.getBytes()));
|
||||
acked.add(new MessageId(b.getBytes()));
|
||||
}
|
||||
// Build and return the ack
|
||||
return packetFactory.createAck(Collections.unmodifiableList(batches));
|
||||
return packetFactory.createAck(Collections.unmodifiableList(acked));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -14,10 +14,11 @@ import net.sf.briar.api.protocol.Group;
|
||||
import net.sf.briar.api.protocol.MessageId;
|
||||
import net.sf.briar.api.protocol.Types;
|
||||
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.CountingConsumer;
|
||||
import net.sf.briar.api.serial.StructReader;
|
||||
import net.sf.briar.api.serial.Reader;
|
||||
import net.sf.briar.api.serial.StructReader;
|
||||
|
||||
class MessageReader implements StructReader<UnverifiedMessage> {
|
||||
|
||||
|
||||
@@ -3,63 +3,44 @@ package net.sf.briar.protocol;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.PublicKey;
|
||||
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.KeyParser;
|
||||
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.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 Collection<UnverifiedMessage> messages;
|
||||
private final MessageDigest batchDigest, messageDigest;
|
||||
private final KeyParser keyParser;
|
||||
|
||||
// Initialise lazily - the batch may contain unsigned messages
|
||||
private KeyParser keyParser = null;
|
||||
private Signature signature = null;
|
||||
|
||||
UnverifiedBatchImpl(CryptoComponent crypto,
|
||||
Collection<UnverifiedMessage> messages) {
|
||||
@Inject
|
||||
MessageVerifierImpl(CryptoComponent crypto) {
|
||||
this.crypto = crypto;
|
||||
this.messages = messages;
|
||||
batchDigest = crypto.getMessageDigest();
|
||||
messageDigest = crypto.getMessageDigest();
|
||||
keyParser = crypto.getSignatureKeyParser();
|
||||
}
|
||||
|
||||
public Batch verify() throws GeneralSecurityException {
|
||||
List<Message> verified = new ArrayList<Message>();
|
||||
for(UnverifiedMessage m : messages) verified.add(verify(m));
|
||||
BatchId id = new BatchId(batchDigest.digest());
|
||||
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);
|
||||
public Message verifyMessage(UnverifiedMessage m)
|
||||
throws GeneralSecurityException {
|
||||
MessageDigest messageDigest = crypto.getMessageDigest();
|
||||
Signature signature = crypto.getSignature();
|
||||
// Hash the message, including the signatures, to get the message ID
|
||||
byte[] raw = m.getSerialised();
|
||||
messageDigest.update(raw);
|
||||
MessageId id = new MessageId(messageDigest.digest());
|
||||
// Verify the author's signature, if there is one
|
||||
Author author = m.getAuthor();
|
||||
if(author != null) {
|
||||
if(keyParser == null) keyParser = crypto.getSignatureKeyParser();
|
||||
PublicKey k = keyParser.parsePublicKey(author.getPublicKey());
|
||||
if(signature == null) signature = crypto.getSignature();
|
||||
signature.initVerify(k);
|
||||
signature.update(raw, 0, m.getLengthSignedByAuthor());
|
||||
if(!signature.verify(m.getAuthorSignature()))
|
||||
@@ -68,9 +49,7 @@ class UnverifiedBatchImpl implements UnverifiedBatch {
|
||||
// Verify the group's signature, if there is one
|
||||
Group group = m.getGroup();
|
||||
if(group != null && group.getPublicKey() != null) {
|
||||
if(keyParser == null) keyParser = crypto.getSignatureKeyParser();
|
||||
PublicKey k = keyParser.parsePublicKey(group.getPublicKey());
|
||||
if(signature == null) signature = crypto.getSignature();
|
||||
signature.initVerify(k);
|
||||
signature.update(raw, 0, m.getLengthSignedByGroup());
|
||||
if(!signature.verify(m.getGroupSignature()))
|
||||
@@ -4,42 +4,23 @@ import java.util.BitSet;
|
||||
import java.util.Collection;
|
||||
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.BatchId;
|
||||
import net.sf.briar.api.protocol.Group;
|
||||
import net.sf.briar.api.protocol.GroupId;
|
||||
import net.sf.briar.api.protocol.MessageId;
|
||||
import net.sf.briar.api.protocol.Offer;
|
||||
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.SubscriptionUpdate;
|
||||
import net.sf.briar.api.protocol.Transport;
|
||||
import net.sf.briar.api.protocol.TransportUpdate;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
class PacketFactoryImpl implements PacketFactory {
|
||||
|
||||
private final CryptoComponent crypto;
|
||||
|
||||
@Inject
|
||||
PacketFactoryImpl(CryptoComponent crypto) {
|
||||
this.crypto = crypto;
|
||||
}
|
||||
|
||||
public Ack createAck(Collection<BatchId> acked) {
|
||||
public Ack createAck(Collection<MessageId> 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) {
|
||||
return new OfferImpl(offered);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import net.sf.briar.api.protocol.AuthorFactory;
|
||||
import net.sf.briar.api.protocol.Group;
|
||||
import net.sf.briar.api.protocol.GroupFactory;
|
||||
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.PacketFactory;
|
||||
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.SubscriptionUpdate;
|
||||
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.serial.StructReader;
|
||||
import net.sf.briar.util.BoundedExecutor;
|
||||
@@ -46,10 +47,10 @@ public class ProtocolModule extends AbstractModule {
|
||||
bind(AuthorFactory.class).to(AuthorFactoryImpl.class);
|
||||
bind(GroupFactory.class).to(GroupFactoryImpl.class);
|
||||
bind(MessageFactory.class).to(MessageFactoryImpl.class);
|
||||
bind(MessageVerifier.class).to(MessageVerifierImpl.class);
|
||||
bind(PacketFactory.class).to(PacketFactoryImpl.class);
|
||||
bind(ProtocolReaderFactory.class).to(ProtocolReaderFactoryImpl.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
|
||||
bind(Executor.class).annotatedWith(
|
||||
VerificationExecutor.class).toInstance(
|
||||
@@ -68,13 +69,6 @@ public class ProtocolModule extends AbstractModule {
|
||||
return new AuthorReader(crypto, authorFactory);
|
||||
}
|
||||
|
||||
@Provides
|
||||
StructReader<UnverifiedBatch> getBatchReader(
|
||||
StructReader<UnverifiedMessage> messageReader,
|
||||
UnverifiedBatchFactory batchFactory) {
|
||||
return new BatchReader(messageReader, batchFactory);
|
||||
}
|
||||
|
||||
@Provides
|
||||
StructReader<Group> getGroupReader(CryptoComponent crypto) {
|
||||
return new GroupReader(crypto);
|
||||
|
||||
@@ -9,9 +9,9 @@ import net.sf.briar.api.protocol.ProtocolReaderFactory;
|
||||
import net.sf.briar.api.protocol.Request;
|
||||
import net.sf.briar.api.protocol.SubscriptionUpdate;
|
||||
import net.sf.briar.api.protocol.TransportUpdate;
|
||||
import net.sf.briar.api.protocol.UnverifiedBatch;
|
||||
import net.sf.briar.api.serial.StructReader;
|
||||
import net.sf.briar.api.protocol.UnverifiedMessage;
|
||||
import net.sf.briar.api.serial.ReaderFactory;
|
||||
import net.sf.briar.api.serial.StructReader;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
@@ -20,7 +20,7 @@ class ProtocolReaderFactoryImpl implements ProtocolReaderFactory {
|
||||
|
||||
private final ReaderFactory readerFactory;
|
||||
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<Request>> requestProvider;
|
||||
private final Provider<StructReader<SubscriptionUpdate>> subscriptionProvider;
|
||||
@@ -29,14 +29,14 @@ class ProtocolReaderFactoryImpl implements ProtocolReaderFactory {
|
||||
@Inject
|
||||
ProtocolReaderFactoryImpl(ReaderFactory readerFactory,
|
||||
Provider<StructReader<Ack>> ackProvider,
|
||||
Provider<StructReader<UnverifiedBatch>> batchProvider,
|
||||
Provider<StructReader<UnverifiedMessage>> messageProvider,
|
||||
Provider<StructReader<Offer>> offerProvider,
|
||||
Provider<StructReader<Request>> requestProvider,
|
||||
Provider<StructReader<SubscriptionUpdate>> subscriptionProvider,
|
||||
Provider<StructReader<TransportUpdate>> transportProvider) {
|
||||
this.readerFactory = readerFactory;
|
||||
this.ackProvider = ackProvider;
|
||||
this.batchProvider = batchProvider;
|
||||
this.messageProvider = messageProvider;
|
||||
this.offerProvider = offerProvider;
|
||||
this.requestProvider = requestProvider;
|
||||
this.subscriptionProvider = subscriptionProvider;
|
||||
@@ -45,7 +45,8 @@ class ProtocolReaderFactoryImpl implements ProtocolReaderFactory {
|
||||
|
||||
public ProtocolReader createProtocolReader(InputStream in) {
|
||||
return new ProtocolReaderImpl(in, readerFactory, ackProvider.get(),
|
||||
batchProvider.get(), offerProvider.get(), requestProvider.get(),
|
||||
subscriptionProvider.get(), transportProvider.get());
|
||||
messageProvider.get(), offerProvider.get(),
|
||||
requestProvider.get(), subscriptionProvider.get(),
|
||||
transportProvider.get());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,10 +10,10 @@ import net.sf.briar.api.protocol.Request;
|
||||
import net.sf.briar.api.protocol.SubscriptionUpdate;
|
||||
import net.sf.briar.api.protocol.TransportUpdate;
|
||||
import net.sf.briar.api.protocol.Types;
|
||||
import net.sf.briar.api.protocol.UnverifiedBatch;
|
||||
import net.sf.briar.api.serial.StructReader;
|
||||
import net.sf.briar.api.protocol.UnverifiedMessage;
|
||||
import net.sf.briar.api.serial.Reader;
|
||||
import net.sf.briar.api.serial.ReaderFactory;
|
||||
import net.sf.briar.api.serial.StructReader;
|
||||
|
||||
class ProtocolReaderImpl implements ProtocolReader {
|
||||
|
||||
@@ -21,14 +21,14 @@ class ProtocolReaderImpl implements ProtocolReader {
|
||||
|
||||
ProtocolReaderImpl(InputStream in, ReaderFactory readerFactory,
|
||||
StructReader<Ack> ackReader,
|
||||
StructReader<UnverifiedBatch> batchReader,
|
||||
StructReader<UnverifiedMessage> messageReader,
|
||||
StructReader<Offer> offerReader,
|
||||
StructReader<Request> requestReader,
|
||||
StructReader<SubscriptionUpdate> subscriptionReader,
|
||||
StructReader<TransportUpdate> transportReader) {
|
||||
reader = readerFactory.createReader(in);
|
||||
reader.addStructReader(Types.ACK, ackReader);
|
||||
reader.addStructReader(Types.BATCH, batchReader);
|
||||
reader.addStructReader(Types.MESSAGE, messageReader);
|
||||
reader.addStructReader(Types.OFFER, offerReader);
|
||||
reader.addStructReader(Types.REQUEST, requestReader);
|
||||
reader.addStructReader(Types.SUBSCRIPTION_UPDATE, subscriptionReader);
|
||||
@@ -47,12 +47,12 @@ class ProtocolReaderImpl implements ProtocolReader {
|
||||
return reader.readStruct(Types.ACK, Ack.class);
|
||||
}
|
||||
|
||||
public boolean hasBatch() throws IOException {
|
||||
return reader.hasStruct(Types.BATCH);
|
||||
public boolean hasMessage() throws IOException {
|
||||
return reader.hasStruct(Types.MESSAGE);
|
||||
}
|
||||
|
||||
public UnverifiedBatch readBatch() throws IOException {
|
||||
return reader.readStruct(Types.BATCH, UnverifiedBatch.class);
|
||||
public UnverifiedMessage readMessage() throws IOException {
|
||||
return reader.readStruct(Types.MESSAGE, UnverifiedMessage.class);
|
||||
}
|
||||
|
||||
public boolean hasOffer() throws IOException {
|
||||
|
||||
@@ -8,13 +8,11 @@ import java.util.BitSet;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
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.GroupId;
|
||||
import net.sf.briar.api.protocol.MessageId;
|
||||
import net.sf.briar.api.protocol.Offer;
|
||||
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.SubscriptionUpdate;
|
||||
import net.sf.briar.api.protocol.Transport;
|
||||
@@ -40,11 +38,11 @@ class ProtocolWriterImpl implements ProtocolWriter {
|
||||
w = writerFactory.createWriter(out);
|
||||
}
|
||||
|
||||
public int getMaxBatchesForAck(long capacity) {
|
||||
public int getMaxMessagesForAck(long capacity) {
|
||||
int packet = (int) Math.min(capacity, MAX_PACKET_LENGTH);
|
||||
int overhead = serial.getSerialisedStructIdLength(Types.ACK)
|
||||
+ serial.getSerialisedListStartLength()
|
||||
+ serial.getSerialisedListEndLength();
|
||||
+ serial.getSerialisedListStartLength()
|
||||
+ serial.getSerialisedListEndLength();
|
||||
int idLength = serial.getSerialisedUniqueIdLength();
|
||||
return (packet - overhead) / idLength;
|
||||
}
|
||||
@@ -52,33 +50,22 @@ class ProtocolWriterImpl implements ProtocolWriter {
|
||||
public int getMaxMessagesForOffer(long capacity) {
|
||||
int packet = (int) Math.min(capacity, MAX_PACKET_LENGTH);
|
||||
int overhead = serial.getSerialisedStructIdLength(Types.OFFER)
|
||||
+ serial.getSerialisedListStartLength()
|
||||
+ serial.getSerialisedListEndLength();
|
||||
+ serial.getSerialisedListStartLength()
|
||||
+ serial.getSerialisedListEndLength();
|
||||
int idLength = serial.getSerialisedUniqueIdLength();
|
||||
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 {
|
||||
w.writeStructId(Types.ACK);
|
||||
w.writeListStart();
|
||||
for(BatchId b : a.getBatchIds()) w.writeBytes(b.getBytes());
|
||||
for(MessageId m : a.getMessageIds()) w.writeBytes(m.getBytes());
|
||||
w.writeListEnd();
|
||||
if(flush) out.flush();
|
||||
}
|
||||
|
||||
public void writeBatch(RawBatch b) throws IOException {
|
||||
w.writeStructId(Types.BATCH);
|
||||
w.writeListStart();
|
||||
for(byte[] raw : b.getMessages()) out.write(raw);
|
||||
w.writeListEnd();
|
||||
public void writeMessage(byte[] raw) throws IOException {
|
||||
out.write(raw);
|
||||
if(flush) out.flush();
|
||||
}
|
||||
|
||||
@@ -111,7 +98,7 @@ class ProtocolWriterImpl implements ProtocolWriter {
|
||||
}
|
||||
|
||||
public void writeSubscriptionUpdate(SubscriptionUpdate s)
|
||||
throws IOException {
|
||||
throws IOException {
|
||||
w.writeStructId(Types.SUBSCRIPTION_UPDATE);
|
||||
// Holes
|
||||
w.writeMapStart();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package net.sf.briar.protocol;
|
||||
import net.sf.briar.api.protocol.Author;
|
||||
import net.sf.briar.api.protocol.Group;
|
||||
import net.sf.briar.api.protocol.MessageId;
|
||||
import net.sf.briar.api.protocol.UnverifiedMessage;
|
||||
|
||||
class UnverifiedMessageImpl implements UnverifiedMessage {
|
||||
|
||||
@@ -52,7 +53,7 @@ class UnverifiedMessageImpl implements UnverifiedMessage {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public byte[] getRaw() {
|
||||
public byte[] getSerialised() {
|
||||
return raw;
|
||||
}
|
||||
|
||||
|
||||
@@ -24,28 +24,28 @@ import net.sf.briar.api.FormatException;
|
||||
import net.sf.briar.api.db.DatabaseComponent;
|
||||
import net.sf.briar.api.db.DatabaseExecutor;
|
||||
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.DatabaseEvent;
|
||||
import net.sf.briar.api.db.event.DatabaseListener;
|
||||
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.plugins.duplex.DuplexTransportConnection;
|
||||
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.MessageVerifier;
|
||||
import net.sf.briar.api.protocol.Offer;
|
||||
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.TransportId;
|
||||
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.transport.ConnectionContext;
|
||||
import net.sf.briar.api.transport.ConnectionReader;
|
||||
@@ -76,6 +76,7 @@ abstract class DuplexConnection implements DatabaseListener {
|
||||
protected final TransportId transportId;
|
||||
|
||||
private final Executor dbExecutor, verificationExecutor;
|
||||
private final MessageVerifier messageVerifier;
|
||||
private final AtomicBoolean canSendOffer, disposed;
|
||||
private final BlockingQueue<Runnable> writerTasks;
|
||||
|
||||
@@ -85,7 +86,8 @@ abstract class DuplexConnection implements DatabaseListener {
|
||||
|
||||
DuplexConnection(@DatabaseExecutor Executor dbExecutor,
|
||||
@VerificationExecutor Executor verificationExecutor,
|
||||
DatabaseComponent db, ConnectionRegistry connRegistry,
|
||||
MessageVerifier messageVerifier, DatabaseComponent db,
|
||||
ConnectionRegistry connRegistry,
|
||||
ConnectionReaderFactory connReaderFactory,
|
||||
ConnectionWriterFactory connWriterFactory,
|
||||
ProtocolReaderFactory protoReaderFactory,
|
||||
@@ -93,6 +95,7 @@ abstract class DuplexConnection implements DatabaseListener {
|
||||
DuplexTransportConnection transport) {
|
||||
this.dbExecutor = dbExecutor;
|
||||
this.verificationExecutor = verificationExecutor;
|
||||
this.messageVerifier = messageVerifier;
|
||||
this.db = db;
|
||||
this.connRegistry = connRegistry;
|
||||
this.connReaderFactory = connReaderFactory;
|
||||
@@ -115,12 +118,12 @@ abstract class DuplexConnection implements DatabaseListener {
|
||||
throws IOException;
|
||||
|
||||
public void eventOccurred(DatabaseEvent e) {
|
||||
if(e instanceof BatchReceivedEvent) {
|
||||
if(e instanceof MessageReceivedEvent) {
|
||||
dbExecutor.execute(new GenerateAcks());
|
||||
} else if(e instanceof ContactRemovedEvent) {
|
||||
ContactId c = ((ContactRemovedEvent) e).getContactId();
|
||||
if(contactId.equals(c)) dispose(false, true);
|
||||
} else if(e instanceof MessagesAddedEvent) {
|
||||
} else if(e instanceof MessageAddedEvent) {
|
||||
if(canSendOffer.getAndSet(false))
|
||||
dbExecutor.execute(new GenerateOffer());
|
||||
} else if(e instanceof SubscriptionsUpdatedEvent) {
|
||||
@@ -142,9 +145,9 @@ abstract class DuplexConnection implements DatabaseListener {
|
||||
if(reader.hasAck()) {
|
||||
Ack a = reader.readAck();
|
||||
dbExecutor.execute(new ReceiveAck(a));
|
||||
} else if(reader.hasBatch()) {
|
||||
UnverifiedBatch b = reader.readBatch();
|
||||
verificationExecutor.execute(new VerifyBatch(b));
|
||||
} else if(reader.hasMessage()) {
|
||||
UnverifiedMessage m = reader.readMessage();
|
||||
verificationExecutor.execute(new VerifyMessage(m));
|
||||
} else if(reader.hasOffer()) {
|
||||
Offer o = reader.readOffer();
|
||||
dbExecutor.execute(new ReceiveOffer(o));
|
||||
@@ -260,18 +263,18 @@ abstract class DuplexConnection implements DatabaseListener {
|
||||
}
|
||||
|
||||
// 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) {
|
||||
this.batch = batch;
|
||||
private VerifyMessage(UnverifiedMessage message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
Batch b = batch.verify();
|
||||
dbExecutor.execute(new ReceiveBatch(b));
|
||||
Message m = messageVerifier.verifyMessage(message);
|
||||
dbExecutor.execute(new ReceiveMessage(m));
|
||||
} catch(GeneralSecurityException 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
|
||||
private class ReceiveBatch implements Runnable {
|
||||
private class ReceiveMessage implements Runnable {
|
||||
|
||||
private final Batch batch;
|
||||
private final Message message;
|
||||
|
||||
private ReceiveBatch(Batch batch) {
|
||||
this.batch = batch;
|
||||
private ReceiveMessage(Message message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
db.receiveBatch(contactId, batch);
|
||||
db.receiveMessage(contactId, message);
|
||||
} catch(DbException e) {
|
||||
if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||
}
|
||||
@@ -394,9 +397,9 @@ abstract class DuplexConnection implements DatabaseListener {
|
||||
|
||||
public void run() {
|
||||
assert writer != null;
|
||||
int maxBatches = writer.getMaxBatchesForAck(Long.MAX_VALUE);
|
||||
int maxMessages = writer.getMaxMessagesForAck(Long.MAX_VALUE);
|
||||
try {
|
||||
Ack a = db.generateAck(contactId, maxBatches);
|
||||
Ack a = db.generateAck(contactId, maxMessages);
|
||||
if(a != null) writerTasks.add(new WriteAck(a));
|
||||
} catch(DbException e) {
|
||||
if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||
@@ -436,11 +439,11 @@ abstract class DuplexConnection implements DatabaseListener {
|
||||
|
||||
public void run() {
|
||||
assert writer != null;
|
||||
int capacity = writer.getMessageCapacityForBatch(Long.MAX_VALUE);
|
||||
try {
|
||||
RawBatch b = db.generateBatch(contactId, capacity, requested);
|
||||
if(b == null) new GenerateOffer().run();
|
||||
else writerTasks.add(new WriteBatch(b, requested));
|
||||
Collection<byte[]> batch = db.generateBatch(contactId,
|
||||
Integer.MAX_VALUE, requested);
|
||||
if(batch == null) new GenerateOffer().run();
|
||||
else writerTasks.add(new WriteBatch(batch, requested));
|
||||
} catch(DbException 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
|
||||
private class WriteBatch implements Runnable {
|
||||
|
||||
private final RawBatch batch;
|
||||
private final Collection<byte[]> batch;
|
||||
private final Collection<MessageId> requested;
|
||||
|
||||
private WriteBatch(RawBatch batch, Collection<MessageId> requested) {
|
||||
private WriteBatch(Collection<byte[]> batch,
|
||||
Collection<MessageId> requested) {
|
||||
this.batch = batch;
|
||||
this.requested = requested;
|
||||
}
|
||||
@@ -461,7 +465,7 @@ abstract class DuplexConnection implements DatabaseListener {
|
||||
public void run() {
|
||||
assert writer != null;
|
||||
try {
|
||||
writer.writeBatch(batch);
|
||||
for(byte[] raw : batch) writer.writeMessage(raw);
|
||||
if(requested.isEmpty()) dbExecutor.execute(new GenerateOffer());
|
||||
else dbExecutor.execute(new GenerateBatches(requested));
|
||||
} catch(IOException e) {
|
||||
|
||||
@@ -10,6 +10,7 @@ import net.sf.briar.api.crypto.KeyManager;
|
||||
import net.sf.briar.api.db.DatabaseComponent;
|
||||
import net.sf.briar.api.db.DatabaseExecutor;
|
||||
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.ProtocolWriterFactory;
|
||||
import net.sf.briar.api.protocol.TransportId;
|
||||
@@ -28,6 +29,7 @@ class DuplexConnectionFactoryImpl implements DuplexConnectionFactory {
|
||||
Logger.getLogger(DuplexConnectionFactoryImpl.class.getName());
|
||||
|
||||
private final Executor dbExecutor, verificationExecutor;
|
||||
private final MessageVerifier messageVerifier;
|
||||
private final DatabaseComponent db;
|
||||
private final KeyManager keyManager;
|
||||
private final ConnectionRegistry connRegistry;
|
||||
@@ -39,13 +41,14 @@ class DuplexConnectionFactoryImpl implements DuplexConnectionFactory {
|
||||
@Inject
|
||||
DuplexConnectionFactoryImpl(@DatabaseExecutor Executor dbExecutor,
|
||||
@VerificationExecutor Executor verificationExecutor,
|
||||
DatabaseComponent db, KeyManager keyManager,
|
||||
ConnectionRegistry connRegistry,
|
||||
MessageVerifier messageVerifier, DatabaseComponent db,
|
||||
KeyManager keyManager, ConnectionRegistry connRegistry,
|
||||
ConnectionReaderFactory connReaderFactory,
|
||||
ConnectionWriterFactory connWriterFactory,
|
||||
ProtocolReaderFactory protoReaderFactory, ProtocolWriterFactory protoWriterFactory) {
|
||||
this.dbExecutor = dbExecutor;
|
||||
this.verificationExecutor = verificationExecutor;
|
||||
this.messageVerifier = messageVerifier;
|
||||
this.db = db;
|
||||
this.keyManager = keyManager;
|
||||
this.connRegistry = connRegistry;
|
||||
@@ -58,9 +61,9 @@ class DuplexConnectionFactoryImpl implements DuplexConnectionFactory {
|
||||
public void createIncomingConnection(ConnectionContext ctx,
|
||||
DuplexTransportConnection transport) {
|
||||
final DuplexConnection conn = new IncomingDuplexConnection(dbExecutor,
|
||||
verificationExecutor, db, connRegistry, connReaderFactory,
|
||||
connWriterFactory, protoReaderFactory, protoWriterFactory, ctx,
|
||||
transport);
|
||||
verificationExecutor, messageVerifier, db, connRegistry,
|
||||
connReaderFactory, connWriterFactory, protoReaderFactory,
|
||||
protoWriterFactory, ctx, transport);
|
||||
Runnable write = new Runnable() {
|
||||
public void run() {
|
||||
conn.write();
|
||||
@@ -84,9 +87,9 @@ class DuplexConnectionFactoryImpl implements DuplexConnectionFactory {
|
||||
return;
|
||||
}
|
||||
final DuplexConnection conn = new OutgoingDuplexConnection(dbExecutor,
|
||||
verificationExecutor, db, connRegistry, connReaderFactory,
|
||||
connWriterFactory, protoReaderFactory, protoWriterFactory, ctx,
|
||||
transport);
|
||||
verificationExecutor, messageVerifier, db, connRegistry,
|
||||
connReaderFactory, connWriterFactory, protoReaderFactory,
|
||||
protoWriterFactory, ctx, transport);
|
||||
Runnable write = new Runnable() {
|
||||
public void run() {
|
||||
conn.write();
|
||||
|
||||
@@ -6,6 +6,7 @@ import java.util.concurrent.Executor;
|
||||
import net.sf.briar.api.db.DatabaseComponent;
|
||||
import net.sf.briar.api.db.DatabaseExecutor;
|
||||
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.ProtocolWriterFactory;
|
||||
import net.sf.briar.api.protocol.VerificationExecutor;
|
||||
@@ -20,15 +21,16 @@ class IncomingDuplexConnection extends DuplexConnection {
|
||||
|
||||
IncomingDuplexConnection(@DatabaseExecutor Executor dbExecutor,
|
||||
@VerificationExecutor Executor verificationExecutor,
|
||||
DatabaseComponent db, ConnectionRegistry connRegistry,
|
||||
MessageVerifier messageVerifier, DatabaseComponent db,
|
||||
ConnectionRegistry connRegistry,
|
||||
ConnectionReaderFactory connReaderFactory,
|
||||
ConnectionWriterFactory connWriterFactory,
|
||||
ProtocolReaderFactory protoReaderFactory,
|
||||
ProtocolWriterFactory protoWriterFactory,
|
||||
ConnectionContext ctx, DuplexTransportConnection transport) {
|
||||
super(dbExecutor, verificationExecutor, db, connRegistry,
|
||||
connReaderFactory, connWriterFactory, protoReaderFactory,
|
||||
protoWriterFactory, ctx, transport);
|
||||
super(dbExecutor, verificationExecutor, messageVerifier, db,
|
||||
connRegistry, connReaderFactory, connWriterFactory,
|
||||
protoReaderFactory, protoWriterFactory, ctx, transport);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -6,6 +6,7 @@ import java.util.concurrent.Executor;
|
||||
import net.sf.briar.api.db.DatabaseComponent;
|
||||
import net.sf.briar.api.db.DatabaseExecutor;
|
||||
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.ProtocolWriterFactory;
|
||||
import net.sf.briar.api.protocol.VerificationExecutor;
|
||||
@@ -20,15 +21,16 @@ class OutgoingDuplexConnection extends DuplexConnection {
|
||||
|
||||
OutgoingDuplexConnection(@DatabaseExecutor Executor dbExecutor,
|
||||
@VerificationExecutor Executor verificationExecutor,
|
||||
DatabaseComponent db, ConnectionRegistry connRegistry,
|
||||
MessageVerifier messageVerifier, DatabaseComponent db,
|
||||
ConnectionRegistry connRegistry,
|
||||
ConnectionReaderFactory connReaderFactory,
|
||||
ConnectionWriterFactory connWriterFactory,
|
||||
ProtocolReaderFactory protoReaderFactory,
|
||||
ProtocolWriterFactory protoWriterFactory, ConnectionContext ctx,
|
||||
DuplexTransportConnection transport) {
|
||||
super(dbExecutor, verificationExecutor, db, connRegistry,
|
||||
connReaderFactory, connWriterFactory, protoReaderFactory,
|
||||
protoWriterFactory, ctx, transport);
|
||||
super(dbExecutor, verificationExecutor, messageVerifier, db,
|
||||
connRegistry, connReaderFactory, connWriterFactory,
|
||||
protoReaderFactory, protoWriterFactory, ctx, transport);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -15,13 +15,14 @@ import net.sf.briar.api.db.DatabaseExecutor;
|
||||
import net.sf.briar.api.db.DbException;
|
||||
import net.sf.briar.api.plugins.simplex.SimplexTransportReader;
|
||||
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.ProtocolReaderFactory;
|
||||
import net.sf.briar.api.protocol.SubscriptionUpdate;
|
||||
import net.sf.briar.api.protocol.TransportId;
|
||||
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.transport.ConnectionContext;
|
||||
import net.sf.briar.api.transport.ConnectionReader;
|
||||
@@ -35,6 +36,7 @@ class IncomingSimplexConnection {
|
||||
Logger.getLogger(IncomingSimplexConnection.class.getName());
|
||||
|
||||
private final Executor dbExecutor, verificationExecutor;
|
||||
private final MessageVerifier messageVerifier;
|
||||
private final DatabaseComponent db;
|
||||
private final ConnectionRegistry connRegistry;
|
||||
private final ConnectionReaderFactory connFactory;
|
||||
@@ -46,12 +48,14 @@ class IncomingSimplexConnection {
|
||||
|
||||
IncomingSimplexConnection(@DatabaseExecutor Executor dbExecutor,
|
||||
@VerificationExecutor Executor verificationExecutor,
|
||||
DatabaseComponent db, ConnectionRegistry connRegistry,
|
||||
MessageVerifier messageVerifier, DatabaseComponent db,
|
||||
ConnectionRegistry connRegistry,
|
||||
ConnectionReaderFactory connFactory,
|
||||
ProtocolReaderFactory protoFactory, ConnectionContext ctx,
|
||||
SimplexTransportReader transport) {
|
||||
this.dbExecutor = dbExecutor;
|
||||
this.verificationExecutor = verificationExecutor;
|
||||
this.messageVerifier = messageVerifier;
|
||||
this.db = db;
|
||||
this.connRegistry = connRegistry;
|
||||
this.connFactory = connFactory;
|
||||
@@ -74,9 +78,9 @@ class IncomingSimplexConnection {
|
||||
if(reader.hasAck()) {
|
||||
Ack a = reader.readAck();
|
||||
dbExecutor.execute(new ReceiveAck(a));
|
||||
} else if(reader.hasBatch()) {
|
||||
UnverifiedBatch b = reader.readBatch();
|
||||
verificationExecutor.execute(new VerifyBatch(b));
|
||||
} else if(reader.hasMessage()) {
|
||||
UnverifiedMessage m = reader.readMessage();
|
||||
verificationExecutor.execute(new VerifyMessage(m));
|
||||
} else if(reader.hasSubscriptionUpdate()) {
|
||||
SubscriptionUpdate s = reader.readSubscriptionUpdate();
|
||||
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) {
|
||||
this.batch = batch;
|
||||
private VerifyMessage(UnverifiedMessage message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
Batch b = batch.verify();
|
||||
dbExecutor.execute(new ReceiveBatch(b));
|
||||
Message m = messageVerifier.verifyMessage(message);
|
||||
dbExecutor.execute(new ReceiveMessage(m));
|
||||
} catch(GeneralSecurityException 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) {
|
||||
this.batch = batch;
|
||||
private ReceiveMessage(Message message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
db.receiveBatch(contactId, batch);
|
||||
db.receiveMessage(contactId, message);
|
||||
} catch(DbException e) {
|
||||
if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import static net.sf.briar.api.protocol.ProtocolConstants.MAX_PACKET_LENGTH;
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Collection;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
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.ProtocolWriter;
|
||||
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.TransportId;
|
||||
import net.sf.briar.api.protocol.TransportUpdate;
|
||||
@@ -77,23 +77,23 @@ class OutgoingSimplexConnection {
|
||||
}
|
||||
// Write acks until you can't write acks no more
|
||||
capacity = conn.getRemainingCapacity();
|
||||
int maxBatches = writer.getMaxBatchesForAck(capacity);
|
||||
Ack a = db.generateAck(contactId, maxBatches);
|
||||
int maxMessages = writer.getMaxMessagesForAck(capacity);
|
||||
Ack a = db.generateAck(contactId, maxMessages);
|
||||
while(a != null) {
|
||||
writer.writeAck(a);
|
||||
capacity = conn.getRemainingCapacity();
|
||||
maxBatches = writer.getMaxBatchesForAck(capacity);
|
||||
a = db.generateAck(contactId, maxBatches);
|
||||
maxMessages = writer.getMaxMessagesForAck(capacity);
|
||||
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 = writer.getMessageCapacityForBatch(capacity);
|
||||
RawBatch b = db.generateBatch(contactId, (int) capacity);
|
||||
while(b != null) {
|
||||
writer.writeBatch(b);
|
||||
int maxLength = (int) Math.min(capacity, MAX_PACKET_LENGTH);
|
||||
Collection<byte[]> batch = db.generateBatch(contactId, maxLength);
|
||||
while(batch != null) {
|
||||
for(byte[] raw : batch) writer.writeMessage(raw);
|
||||
capacity = conn.getRemainingCapacity();
|
||||
capacity = writer.getMessageCapacityForBatch(capacity);
|
||||
b = db.generateBatch(contactId, (int) capacity);
|
||||
maxLength = (int) Math.min(capacity, MAX_PACKET_LENGTH);
|
||||
batch = db.generateBatch(contactId, maxLength);
|
||||
}
|
||||
writer.flush();
|
||||
writer.close();
|
||||
|
||||
@@ -11,6 +11,7 @@ import net.sf.briar.api.db.DatabaseComponent;
|
||||
import net.sf.briar.api.db.DatabaseExecutor;
|
||||
import net.sf.briar.api.plugins.simplex.SimplexTransportReader;
|
||||
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.ProtocolWriterFactory;
|
||||
import net.sf.briar.api.protocol.TransportId;
|
||||
@@ -29,6 +30,7 @@ class SimplexConnectionFactoryImpl implements SimplexConnectionFactory {
|
||||
Logger.getLogger(SimplexConnectionFactoryImpl.class.getName());
|
||||
|
||||
private final Executor dbExecutor, verificationExecutor;
|
||||
private final MessageVerifier messageVerifier;
|
||||
private final DatabaseComponent db;
|
||||
private final KeyManager keyManager;
|
||||
private final ConnectionRegistry connRegistry;
|
||||
@@ -40,14 +42,15 @@ class SimplexConnectionFactoryImpl implements SimplexConnectionFactory {
|
||||
@Inject
|
||||
SimplexConnectionFactoryImpl(@DatabaseExecutor Executor dbExecutor,
|
||||
@VerificationExecutor Executor verificationExecutor,
|
||||
DatabaseComponent db, KeyManager keyManager,
|
||||
ConnectionRegistry connRegistry,
|
||||
MessageVerifier messageVerifier, DatabaseComponent db,
|
||||
KeyManager keyManager, ConnectionRegistry connRegistry,
|
||||
ConnectionReaderFactory connReaderFactory,
|
||||
ConnectionWriterFactory connWriterFactory,
|
||||
ProtocolReaderFactory protoReaderFactory,
|
||||
ProtocolWriterFactory protoWriterFactory) {
|
||||
this.dbExecutor = dbExecutor;
|
||||
this.verificationExecutor = verificationExecutor;
|
||||
this.messageVerifier = messageVerifier;
|
||||
this.db = db;
|
||||
this.keyManager = keyManager;
|
||||
this.connRegistry = connRegistry;
|
||||
@@ -59,8 +62,8 @@ class SimplexConnectionFactoryImpl implements SimplexConnectionFactory {
|
||||
|
||||
public void createIncomingConnection(ConnectionContext ctx, SimplexTransportReader r) {
|
||||
final IncomingSimplexConnection conn = new IncomingSimplexConnection(
|
||||
dbExecutor, verificationExecutor, db, connRegistry,
|
||||
connReaderFactory, protoReaderFactory, ctx, r);
|
||||
dbExecutor, verificationExecutor, messageVerifier, db,
|
||||
connRegistry, connReaderFactory, protoReaderFactory, ctx, r);
|
||||
Runnable read = new Runnable() {
|
||||
public void run() {
|
||||
conn.read();
|
||||
|
||||
@@ -89,14 +89,11 @@
|
||||
<test name='net.sf.briar.plugins.modem.ModemPluginTest'/>
|
||||
<test name='net.sf.briar.plugins.tcp.LanTcpPluginTest'/>
|
||||
<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.ConsumersTest'/>
|
||||
<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.RequestReaderTest'/>
|
||||
<test name='net.sf.briar.protocol.UnverifiedBatchImplTest'/>
|
||||
<test name='net.sf.briar.protocol.simplex.OutgoingSimplexConnectionTest'/>
|
||||
<test name='net.sf.briar.protocol.simplex.SimplexProtocolIntegrationTest'/>
|
||||
<test name='net.sf.briar.serial.ReaderImplTest'/>
|
||||
|
||||
@@ -12,7 +12,6 @@ import java.util.Arrays;
|
||||
import java.util.BitSet;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
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.Author;
|
||||
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.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.MessageId;
|
||||
import net.sf.briar.api.protocol.MessageVerifier;
|
||||
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.api.protocol.UnverifiedMessage;
|
||||
import net.sf.briar.api.transport.ConnectionContext;
|
||||
import net.sf.briar.api.transport.ConnectionReader;
|
||||
import net.sf.briar.api.transport.ConnectionReaderFactory;
|
||||
@@ -64,15 +62,13 @@ import com.google.inject.Injector;
|
||||
|
||||
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 ConnectionWriterFactory connectionWriterFactory;
|
||||
private final ProtocolReaderFactory protocolReaderFactory;
|
||||
private final ProtocolWriterFactory protocolWriterFactory;
|
||||
private final PacketFactory packetFactory;
|
||||
private final CryptoComponent crypto;
|
||||
private final MessageVerifier messageVerifier;
|
||||
|
||||
private final ContactId contactId;
|
||||
private final TransportId transportId;
|
||||
private final byte[] secret;
|
||||
@@ -82,30 +78,32 @@ public class ProtocolIntegrationTest extends BriarTestCase {
|
||||
private final String authorName = "Alice";
|
||||
private final String subject = "Hello";
|
||||
private final String messageBody = "Hello world";
|
||||
private final Collection<MessageId> messageIds;
|
||||
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 DatabaseModule(), new LifecycleModule(),
|
||||
new ProtocolModule(), new SerialModule(),
|
||||
new TestDatabaseModule(), new SimplexProtocolModule(),
|
||||
new TransportModule(), new DuplexProtocolModule());
|
||||
Injector i = Guice.createInjector(new TestDatabaseModule(),
|
||||
new ClockModule(), new CryptoModule(), new DatabaseModule(),
|
||||
new LifecycleModule(), new ProtocolModule(),
|
||||
new DuplexProtocolModule(), new SimplexProtocolModule(),
|
||||
new SerialModule(), new TransportModule());
|
||||
connectionReaderFactory = i.getInstance(ConnectionReaderFactory.class);
|
||||
connectionWriterFactory = i.getInstance(ConnectionWriterFactory.class);
|
||||
protocolReaderFactory = i.getInstance(ProtocolReaderFactory.class);
|
||||
protocolWriterFactory = i.getInstance(ProtocolWriterFactory.class);
|
||||
packetFactory = i.getInstance(PacketFactory.class);
|
||||
crypto = i.getInstance(CryptoComponent.class);
|
||||
messageVerifier = i.getInstance(MessageVerifier.class);
|
||||
contactId = new ContactId(234);
|
||||
transportId = new TransportId(TestUtils.getRandomId());
|
||||
// Create a shared secret
|
||||
Random r = new Random();
|
||||
secret = new byte[32];
|
||||
r.nextBytes(secret);
|
||||
new Random().nextBytes(secret);
|
||||
// Create two groups: one restricted, one unrestricted
|
||||
GroupFactory groupFactory = i.getInstance(GroupFactory.class);
|
||||
group = groupFactory.createGroup("Unrestricted group", null);
|
||||
CryptoComponent crypto = i.getInstance(CryptoComponent.class);
|
||||
KeyPair groupKeyPair = crypto.generateSignatureKeyPair();
|
||||
group1 = groupFactory.createGroup("Restricted group",
|
||||
groupKeyPair.getPublic().getEncoded());
|
||||
@@ -127,6 +125,8 @@ public class ProtocolIntegrationTest extends BriarTestCase {
|
||||
message3 = messageFactory.createMessage(null, group1,
|
||||
groupKeyPair.getPrivate(), author, authorKeyPair.getPrivate(),
|
||||
subject, messageBody.getBytes("UTF-8"));
|
||||
messageIds = Arrays.asList(message.getId(),
|
||||
message1.getId(), message2.getId(), message3.getId());
|
||||
// Create some transports
|
||||
TransportId transportId = new TransportId(TestUtils.getRandomId());
|
||||
Transport transport = new Transport(transportId,
|
||||
@@ -149,18 +149,15 @@ public class ProtocolIntegrationTest extends BriarTestCase {
|
||||
ProtocolWriter writer = protocolWriterFactory.createProtocolWriter(out1,
|
||||
false);
|
||||
|
||||
Ack a = packetFactory.createAck(Collections.singletonList(ack));
|
||||
Ack a = packetFactory.createAck(messageIds);
|
||||
writer.writeAck(a);
|
||||
|
||||
Collection<byte[]> batch = Arrays.asList(message.getSerialised(),
|
||||
message1.getSerialised(), message2.getSerialised(),
|
||||
message3.getSerialised());
|
||||
RawBatch b = packetFactory.createBatch(batch);
|
||||
writer.writeBatch(b);
|
||||
writer.writeMessage(message.getSerialised());
|
||||
writer.writeMessage(message1.getSerialised());
|
||||
writer.writeMessage(message2.getSerialised());
|
||||
writer.writeMessage(message3.getSerialised());
|
||||
|
||||
Collection<MessageId> offer = Arrays.asList(message.getId(),
|
||||
message1.getId(), message2.getId(), message3.getId());
|
||||
Offer o = packetFactory.createOffer(offer);
|
||||
Offer o = packetFactory.createOffer(messageIds);
|
||||
writer.writeOffer(o);
|
||||
|
||||
BitSet requested = new BitSet(4);
|
||||
@@ -200,29 +197,26 @@ public class ProtocolIntegrationTest extends BriarTestCase {
|
||||
// Read the ack
|
||||
assertTrue(reader.hasAck());
|
||||
Ack a = reader.readAck();
|
||||
assertEquals(Collections.singletonList(ack), a.getBatchIds());
|
||||
assertEquals(messageIds, a.getMessageIds());
|
||||
|
||||
// Read and verify the batch
|
||||
assertTrue(reader.hasBatch());
|
||||
Batch b = reader.readBatch().verify();
|
||||
Collection<Message> messages = b.getMessages();
|
||||
assertEquals(4, messages.size());
|
||||
Iterator<Message> it = messages.iterator();
|
||||
checkMessageEquality(message, it.next());
|
||||
checkMessageEquality(message1, it.next());
|
||||
checkMessageEquality(message2, it.next());
|
||||
checkMessageEquality(message3, it.next());
|
||||
// Read and verify the messages
|
||||
assertTrue(reader.hasMessage());
|
||||
UnverifiedMessage m = reader.readMessage();
|
||||
checkMessageEquality(message, messageVerifier.verifyMessage(m));
|
||||
assertTrue(reader.hasMessage());
|
||||
m = reader.readMessage();
|
||||
checkMessageEquality(message1, messageVerifier.verifyMessage(m));
|
||||
assertTrue(reader.hasMessage());
|
||||
m = reader.readMessage();
|
||||
checkMessageEquality(message2, messageVerifier.verifyMessage(m));
|
||||
assertTrue(reader.hasMessage());
|
||||
m = reader.readMessage();
|
||||
checkMessageEquality(message3, messageVerifier.verifyMessage(m));
|
||||
|
||||
// Read the offer
|
||||
assertTrue(reader.hasOffer());
|
||||
Offer o = reader.readOffer();
|
||||
Collection<MessageId> offered = 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());
|
||||
assertEquals(messageIds, o.getMessageIds());
|
||||
|
||||
// Read the request
|
||||
assertTrue(reader.hasRequest());
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package net.sf.briar.db;
|
||||
package net.sf.briar;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
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.MessageId;
|
||||
|
||||
class TestMessage implements Message {
|
||||
public class TestMessage implements Message {
|
||||
|
||||
private final MessageId id, parent;
|
||||
private final GroupId group;
|
||||
@@ -7,6 +7,7 @@ import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import net.sf.briar.BriarTestCase;
|
||||
import net.sf.briar.TestMessage;
|
||||
import net.sf.briar.TestUtils;
|
||||
import net.sf.briar.api.ContactId;
|
||||
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.ContactRemovedEvent;
|
||||
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.SubscriptionsUpdatedEvent;
|
||||
import net.sf.briar.api.lifecycle.ShutdownManager;
|
||||
import net.sf.briar.api.protocol.Ack;
|
||||
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.Offer;
|
||||
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.SubscriptionUpdate;
|
||||
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 AuthorId authorId;
|
||||
protected final BatchId batchId;
|
||||
protected final ContactId contactId;
|
||||
protected final GroupId groupId;
|
||||
protected final MessageId messageId, parentId;
|
||||
protected final MessageId messageId, messageId1;
|
||||
private final String subject;
|
||||
private final long timestamp;
|
||||
private final int size;
|
||||
@@ -66,11 +63,10 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
|
||||
public DatabaseComponentTest() {
|
||||
super();
|
||||
authorId = new AuthorId(TestUtils.getRandomId());
|
||||
batchId = new BatchId(TestUtils.getRandomId());
|
||||
contactId = new ContactId(234);
|
||||
groupId = new GroupId(TestUtils.getRandomId());
|
||||
messageId = new MessageId(TestUtils.getRandomId());
|
||||
parentId = new MessageId(TestUtils.getRandomId());
|
||||
messageId1 = new MessageId(TestUtils.getRandomId());
|
||||
subject = "Foo";
|
||||
timestamp = System.currentTimeMillis();
|
||||
size = 1234;
|
||||
@@ -250,11 +246,11 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
|
||||
oneOf(database).setSendability(txn, messageId, 1);
|
||||
// The parent exists, is in the DB, and is in the same group
|
||||
oneOf(database).getGroupMessageParent(txn, messageId);
|
||||
will(returnValue(parentId));
|
||||
will(returnValue(messageId1));
|
||||
// The parent is already sendable
|
||||
oneOf(database).getSendability(txn, parentId);
|
||||
oneOf(database).getSendability(txn, messageId1);
|
||||
will(returnValue(1));
|
||||
oneOf(database).setSendability(txn, parentId, 2);
|
||||
oneOf(database).setSendability(txn, messageId1, 2);
|
||||
oneOf(database).commitTransaction(txn);
|
||||
}});
|
||||
DatabaseComponent db = createDatabaseComponent(database, cleaner,
|
||||
@@ -288,13 +284,13 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
|
||||
oneOf(database).setSendability(txn, messageId, 1);
|
||||
// The parent exists, is in the DB, and is in the same group
|
||||
oneOf(database).getGroupMessageParent(txn, messageId);
|
||||
will(returnValue(parentId));
|
||||
will(returnValue(messageId1));
|
||||
// The parent is not already sendable
|
||||
oneOf(database).getSendability(txn, parentId);
|
||||
oneOf(database).getSendability(txn, messageId1);
|
||||
will(returnValue(0));
|
||||
oneOf(database).setSendability(txn, parentId, 1);
|
||||
oneOf(database).setSendability(txn, messageId1, 1);
|
||||
// The parent has no parent
|
||||
oneOf(database).getGroupMessageParent(txn, parentId);
|
||||
oneOf(database).getGroupMessageParent(txn, messageId1);
|
||||
will(returnValue(null));
|
||||
oneOf(database).commitTransaction(txn);
|
||||
}});
|
||||
@@ -494,7 +490,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
|
||||
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
|
||||
final PacketFactory packetFactory = context.mock(PacketFactory.class);
|
||||
final Ack ack = context.mock(Ack.class);
|
||||
final Batch batch = context.mock(Batch.class);
|
||||
final Offer offer = context.mock(Offer.class);
|
||||
final SubscriptionUpdate subscriptionUpdate =
|
||||
context.mock(SubscriptionUpdate.class);
|
||||
@@ -563,7 +558,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
|
||||
} catch(NoSuchContactException expected) {}
|
||||
|
||||
try {
|
||||
db.receiveBatch(contactId, batch);
|
||||
db.receiveMessage(contactId, message);
|
||||
fail();
|
||||
} catch(NoSuchContactException expected) {}
|
||||
|
||||
@@ -631,10 +626,8 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
|
||||
|
||||
@Test
|
||||
public void testGenerateAck() throws Exception {
|
||||
final BatchId batchId1 = new BatchId(TestUtils.getRandomId());
|
||||
final Collection<BatchId> batchesToAck = new ArrayList<BatchId>();
|
||||
batchesToAck.add(batchId);
|
||||
batchesToAck.add(batchId1);
|
||||
final Collection<MessageId> messagesToAck = Arrays.asList(messageId,
|
||||
messageId1);
|
||||
Mockery context = new Mockery();
|
||||
@SuppressWarnings("unchecked")
|
||||
final Database<Object> database = context.mock(Database.class);
|
||||
@@ -648,14 +641,14 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
|
||||
allowing(database).commitTransaction(txn);
|
||||
allowing(database).containsContact(txn, contactId);
|
||||
will(returnValue(true));
|
||||
// Get the batches to ack
|
||||
oneOf(database).getBatchesToAck(txn, contactId, 123);
|
||||
will(returnValue(batchesToAck));
|
||||
// Create the packet
|
||||
oneOf(packetFactory).createAck(batchesToAck);
|
||||
// Get the messages to ack
|
||||
oneOf(database).getMessagesToAck(txn, contactId, 123);
|
||||
will(returnValue(messagesToAck));
|
||||
// Create the ack packet
|
||||
oneOf(packetFactory).createAck(messagesToAck);
|
||||
will(returnValue(ack));
|
||||
// Record the batches that were acked
|
||||
oneOf(database).removeBatchesToAck(txn, contactId, batchesToAck);
|
||||
// Record the messages that were acked
|
||||
oneOf(database).removeMessagesToAck(txn, contactId, messagesToAck);
|
||||
}});
|
||||
DatabaseComponent db = createDatabaseComponent(database, cleaner,
|
||||
shutdown, packetFactory);
|
||||
@@ -667,7 +660,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
|
||||
|
||||
@Test
|
||||
public void testGenerateBatch() throws Exception {
|
||||
final MessageId messageId1 = new MessageId(TestUtils.getRandomId());
|
||||
final byte[] raw1 = new byte[size];
|
||||
final Collection<MessageId> sendable = Arrays.asList(messageId,
|
||||
messageId1);
|
||||
@@ -678,7 +670,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
|
||||
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
||||
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
|
||||
final PacketFactory packetFactory = context.mock(PacketFactory.class);
|
||||
final RawBatch batch = context.mock(RawBatch.class);
|
||||
context.checking(new Expectations() {{
|
||||
allowing(database).startTransaction();
|
||||
will(returnValue(txn));
|
||||
@@ -692,40 +683,32 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
|
||||
will(returnValue(raw));
|
||||
oneOf(database).getMessage(txn, messageId1);
|
||||
will(returnValue(raw1));
|
||||
// Create the packet
|
||||
oneOf(packetFactory).createBatch(messages);
|
||||
will(returnValue(batch));
|
||||
// Record the outstanding batch
|
||||
oneOf(batch).getId();
|
||||
will(returnValue(batchId));
|
||||
oneOf(database).addOutstandingBatch(txn, contactId, batchId,
|
||||
sendable);
|
||||
// Record the outstanding messages
|
||||
oneOf(database).addOutstandingMessages(txn, contactId, sendable);
|
||||
}});
|
||||
DatabaseComponent db = createDatabaseComponent(database, cleaner,
|
||||
shutdown, packetFactory);
|
||||
|
||||
assertEquals(batch, db.generateBatch(contactId, size * 2));
|
||||
assertEquals(messages, db.generateBatch(contactId, size * 2));
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenerateBatchFromRequest() throws Exception {
|
||||
final MessageId messageId1 = new MessageId(TestUtils.getRandomId());
|
||||
final MessageId messageId2 = new MessageId(TestUtils.getRandomId());
|
||||
final byte[] raw1 = new byte[size];
|
||||
final Collection<MessageId> requested = new ArrayList<MessageId>();
|
||||
requested.add(messageId);
|
||||
requested.add(messageId1);
|
||||
requested.add(messageId2);
|
||||
final Collection<byte[]> msgs = Arrays.asList(raw1);
|
||||
final Collection<byte[]> messages = Arrays.asList(raw1);
|
||||
Mockery context = new Mockery();
|
||||
@SuppressWarnings("unchecked")
|
||||
final Database<Object> database = context.mock(Database.class);
|
||||
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
||||
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
|
||||
final PacketFactory packetFactory = context.mock(PacketFactory.class);
|
||||
final RawBatch batch = context.mock(RawBatch.class);
|
||||
context.checking(new Expectations() {{
|
||||
allowing(database).startTransaction();
|
||||
will(returnValue(txn));
|
||||
@@ -739,19 +722,15 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
|
||||
will(returnValue(raw1)); // Message is sendable
|
||||
oneOf(database).getMessageIfSendable(txn, contactId, messageId2);
|
||||
will(returnValue(null)); // Message is not sendable
|
||||
// Create the packet
|
||||
oneOf(packetFactory).createBatch(msgs);
|
||||
will(returnValue(batch));
|
||||
// Record the outstanding batch
|
||||
oneOf(batch).getId();
|
||||
will(returnValue(batchId));
|
||||
oneOf(database).addOutstandingBatch(txn, contactId, batchId,
|
||||
// Record the outstanding messages
|
||||
oneOf(database).addOutstandingMessages(txn, contactId,
|
||||
Collections.singletonList(messageId1));
|
||||
}});
|
||||
DatabaseComponent db = createDatabaseComponent(database, cleaner,
|
||||
shutdown, packetFactory);
|
||||
|
||||
assertEquals(batch, db.generateBatch(contactId, size * 3, requested));
|
||||
assertEquals(messages, db.generateBatch(contactId, size * 3,
|
||||
requested));
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
@@ -903,7 +882,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
|
||||
|
||||
@Test
|
||||
public void testReceiveAck() throws Exception {
|
||||
final BatchId batchId1 = new BatchId(TestUtils.getRandomId());
|
||||
Mockery context = new Mockery();
|
||||
@SuppressWarnings("unchecked")
|
||||
final Database<Object> database = context.mock(Database.class);
|
||||
@@ -917,14 +895,14 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
|
||||
allowing(database).commitTransaction(txn);
|
||||
allowing(database).containsContact(txn, contactId);
|
||||
will(returnValue(true));
|
||||
// Get the acked batches
|
||||
oneOf(ack).getBatchIds();
|
||||
will(returnValue(Collections.singletonList(batchId)));
|
||||
oneOf(database).removeAckedBatch(txn, contactId, batchId);
|
||||
// Find lost batches
|
||||
oneOf(database).getLostBatches(txn, contactId);
|
||||
will(returnValue(Collections.singletonList(batchId1)));
|
||||
oneOf(database).removeLostBatch(txn, contactId, batchId1);
|
||||
// Get the acked messages
|
||||
oneOf(ack).getMessageIds();
|
||||
will(returnValue(Collections.singletonList(messageId)));
|
||||
oneOf(database).removeAckedMessages(txn, contactId,
|
||||
Collections.singletonList(messageId));
|
||||
// Find lost messages
|
||||
oneOf(database).getLostMessages(txn, contactId);
|
||||
will(returnValue(Collections.emptyList()));
|
||||
}});
|
||||
DatabaseComponent db = createDatabaseComponent(database, cleaner,
|
||||
shutdown, packetFactory);
|
||||
@@ -935,74 +913,64 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReceiveBatchStoresPrivateMessage() throws Exception {
|
||||
public void testReceivePrivateMessage() throws Exception {
|
||||
Mockery context = new Mockery();
|
||||
@SuppressWarnings("unchecked")
|
||||
final Database<Object> database = context.mock(Database.class);
|
||||
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
||||
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
|
||||
final PacketFactory packetFactory = context.mock(PacketFactory.class);
|
||||
final Batch batch = context.mock(Batch.class);
|
||||
context.checking(new Expectations() {{
|
||||
allowing(database).startTransaction();
|
||||
will(returnValue(txn));
|
||||
allowing(database).commitTransaction(txn);
|
||||
allowing(database).containsContact(txn, contactId);
|
||||
will(returnValue(true));
|
||||
oneOf(batch).getMessages();
|
||||
will(returnValue(Collections.singletonList(privateMessage)));
|
||||
// The message is stored
|
||||
oneOf(database).addPrivateMessage(txn, privateMessage, contactId);
|
||||
will(returnValue(true));
|
||||
oneOf(database).setStatus(txn, contactId, messageId, Status.SEEN);
|
||||
// The batch must be acked
|
||||
oneOf(batch).getId();
|
||||
will(returnValue(batchId));
|
||||
oneOf(database).addBatchToAck(txn, contactId, batchId);
|
||||
// The message must be acked
|
||||
oneOf(database).addMessageToAck(txn, contactId, messageId);
|
||||
}});
|
||||
DatabaseComponent db = createDatabaseComponent(database, cleaner,
|
||||
shutdown, packetFactory);
|
||||
|
||||
db.receiveBatch(contactId, batch);
|
||||
db.receiveMessage(contactId, privateMessage);
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReceiveBatchWithDuplicatePrivateMessage() throws Exception {
|
||||
public void testReceiveDuplicatePrivateMessage() throws Exception {
|
||||
Mockery context = new Mockery();
|
||||
@SuppressWarnings("unchecked")
|
||||
final Database<Object> database = context.mock(Database.class);
|
||||
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
||||
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
|
||||
final PacketFactory packetFactory = context.mock(PacketFactory.class);
|
||||
final Batch batch = context.mock(Batch.class);
|
||||
context.checking(new Expectations() {{
|
||||
allowing(database).startTransaction();
|
||||
will(returnValue(txn));
|
||||
allowing(database).commitTransaction(txn);
|
||||
allowing(database).containsContact(txn, contactId);
|
||||
will(returnValue(true));
|
||||
oneOf(batch).getMessages();
|
||||
will(returnValue(Collections.singletonList(privateMessage)));
|
||||
// The message is stored, but it's a duplicate
|
||||
oneOf(database).addPrivateMessage(txn, privateMessage, contactId);
|
||||
will(returnValue(false));
|
||||
// The batch must still be acked
|
||||
oneOf(batch).getId();
|
||||
will(returnValue(batchId));
|
||||
oneOf(database).addBatchToAck(txn, contactId, batchId);
|
||||
// The message must still be acked
|
||||
oneOf(database).addMessageToAck(txn, contactId, messageId);
|
||||
}});
|
||||
DatabaseComponent db = createDatabaseComponent(database, cleaner,
|
||||
shutdown, packetFactory);
|
||||
|
||||
db.receiveBatch(contactId, batch);
|
||||
db.receiveMessage(contactId, privateMessage);
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReceiveBatchDoesNotStoreGroupMessageUnlessSubscribed()
|
||||
public void testReceiveMessageDoesNotStoreGroupMessageUnlessSubscribed()
|
||||
throws Exception {
|
||||
Mockery context = new Mockery();
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -1010,7 +978,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
|
||||
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
||||
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
|
||||
final PacketFactory packetFactory = context.mock(PacketFactory.class);
|
||||
final Batch batch = context.mock(Batch.class);
|
||||
context.checking(new Expectations() {{
|
||||
allowing(database).startTransaction();
|
||||
will(returnValue(txn));
|
||||
@@ -1018,26 +985,22 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
|
||||
allowing(database).containsContact(txn, contactId);
|
||||
will(returnValue(true));
|
||||
// Only store messages belonging to visible, subscribed groups
|
||||
oneOf(batch).getMessages();
|
||||
will(returnValue(Collections.singletonList(message)));
|
||||
oneOf(database).containsVisibleSubscription(txn, groupId,
|
||||
contactId, timestamp);
|
||||
will(returnValue(false));
|
||||
// The message is not stored but the batch must still be acked
|
||||
oneOf(batch).getId();
|
||||
will(returnValue(batchId));
|
||||
oneOf(database).addBatchToAck(txn, contactId, batchId);
|
||||
// The message is not stored but it must still be acked
|
||||
oneOf(database).addMessageToAck(txn, contactId, messageId);
|
||||
}});
|
||||
DatabaseComponent db = createDatabaseComponent(database, cleaner,
|
||||
shutdown, packetFactory);
|
||||
|
||||
db.receiveBatch(contactId, batch);
|
||||
db.receiveMessage(contactId, message);
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReceiveBatchDoesNotCalculateSendabilityForDuplicates()
|
||||
public void testReceiveMessageDoesNotCalculateSendabilityForDuplicates()
|
||||
throws Exception {
|
||||
Mockery context = new Mockery();
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -1045,7 +1008,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
|
||||
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
||||
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
|
||||
final PacketFactory packetFactory = context.mock(PacketFactory.class);
|
||||
final Batch batch = context.mock(Batch.class);
|
||||
context.checking(new Expectations() {{
|
||||
allowing(database).startTransaction();
|
||||
will(returnValue(txn));
|
||||
@@ -1053,8 +1015,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
|
||||
allowing(database).containsContact(txn, contactId);
|
||||
will(returnValue(true));
|
||||
// Only store messages belonging to visible, subscribed groups
|
||||
oneOf(batch).getMessages();
|
||||
will(returnValue(Collections.singletonList(message)));
|
||||
oneOf(database).containsVisibleSubscription(txn, groupId,
|
||||
contactId, timestamp);
|
||||
will(returnValue(true));
|
||||
@@ -1062,28 +1022,25 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
|
||||
oneOf(database).addGroupMessage(txn, message);
|
||||
will(returnValue(false));
|
||||
oneOf(database).setStatus(txn, contactId, messageId, Status.SEEN);
|
||||
// The batch needs to be acknowledged
|
||||
oneOf(batch).getId();
|
||||
will(returnValue(batchId));
|
||||
oneOf(database).addBatchToAck(txn, contactId, batchId);
|
||||
// The message must be acked
|
||||
oneOf(database).addMessageToAck(txn, contactId, messageId);
|
||||
}});
|
||||
DatabaseComponent db = createDatabaseComponent(database, cleaner,
|
||||
shutdown, packetFactory);
|
||||
|
||||
db.receiveBatch(contactId, batch);
|
||||
db.receiveMessage(contactId, message);
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReceiveBatchCalculatesSendability() throws Exception {
|
||||
public void testReceiveMessageCalculatesSendability() throws Exception {
|
||||
Mockery context = new Mockery();
|
||||
@SuppressWarnings("unchecked")
|
||||
final Database<Object> database = context.mock(Database.class);
|
||||
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
||||
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
|
||||
final PacketFactory packetFactory = context.mock(PacketFactory.class);
|
||||
final Batch batch = context.mock(Batch.class);
|
||||
context.checking(new Expectations() {{
|
||||
allowing(database).startTransaction();
|
||||
will(returnValue(txn));
|
||||
@@ -1091,8 +1048,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
|
||||
allowing(database).containsContact(txn, contactId);
|
||||
will(returnValue(true));
|
||||
// Only store messages belonging to visible, subscribed groups
|
||||
oneOf(batch).getMessages();
|
||||
will(returnValue(Collections.singletonList(message)));
|
||||
oneOf(database).containsVisibleSubscription(txn, groupId,
|
||||
contactId, timestamp);
|
||||
will(returnValue(true));
|
||||
@@ -1109,28 +1064,26 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
|
||||
oneOf(database).getNumberOfSendableChildren(txn, messageId);
|
||||
will(returnValue(0));
|
||||
oneOf(database).setSendability(txn, messageId, 0);
|
||||
// The batch needs to be acknowledged
|
||||
oneOf(batch).getId();
|
||||
will(returnValue(batchId));
|
||||
oneOf(database).addBatchToAck(txn, contactId, batchId);
|
||||
// The message must be acked
|
||||
oneOf(database).addMessageToAck(txn, contactId, messageId);
|
||||
}});
|
||||
DatabaseComponent db = createDatabaseComponent(database, cleaner,
|
||||
shutdown, packetFactory);
|
||||
|
||||
db.receiveBatch(contactId, batch);
|
||||
db.receiveMessage(contactId, message);
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReceiveBatchUpdatesAncestorSendability() throws Exception {
|
||||
public void testReceiveMessageUpdatesAncestorSendability()
|
||||
throws Exception {
|
||||
Mockery context = new Mockery();
|
||||
@SuppressWarnings("unchecked")
|
||||
final Database<Object> database = context.mock(Database.class);
|
||||
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
||||
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
|
||||
final PacketFactory packetFactory = context.mock(PacketFactory.class);
|
||||
final Batch batch = context.mock(Batch.class);
|
||||
context.checking(new Expectations() {{
|
||||
allowing(database).startTransaction();
|
||||
will(returnValue(txn));
|
||||
@@ -1138,8 +1091,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
|
||||
allowing(database).containsContact(txn, contactId);
|
||||
will(returnValue(true));
|
||||
// Only store messages belonging to visible, subscribed groups
|
||||
oneOf(batch).getMessages();
|
||||
will(returnValue(Collections.singletonList(message)));
|
||||
oneOf(database).containsVisibleSubscription(txn, groupId,
|
||||
contactId, timestamp);
|
||||
will(returnValue(true));
|
||||
@@ -1158,15 +1109,13 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
|
||||
oneOf(database).setSendability(txn, messageId, 2);
|
||||
oneOf(database).getGroupMessageParent(txn, messageId);
|
||||
will(returnValue(null));
|
||||
// The batch needs to be acknowledged
|
||||
oneOf(batch).getId();
|
||||
will(returnValue(batchId));
|
||||
oneOf(database).addBatchToAck(txn, contactId, batchId);
|
||||
// The message must be acked
|
||||
oneOf(database).addMessageToAck(txn, contactId, messageId);
|
||||
}});
|
||||
DatabaseComponent db = createDatabaseComponent(database, cleaner,
|
||||
shutdown, packetFactory);
|
||||
|
||||
db.receiveBatch(contactId, batch);
|
||||
db.receiveMessage(contactId, message);
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
@@ -1319,7 +1268,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
|
||||
oneOf(database).setSendability(txn, messageId, 0);
|
||||
oneOf(database).commitTransaction(txn);
|
||||
// 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,
|
||||
shutdown, packetFactory);
|
||||
@@ -1350,7 +1299,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
|
||||
will(returnValue(true));
|
||||
oneOf(database).setStatus(txn, contactId, messageId, Status.NEW);
|
||||
// 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,
|
||||
shutdown, packetFactory);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package net.sf.briar.db;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
import static net.sf.briar.db.DatabaseConstants.RETRANSMIT_THRESHOLD;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
|
||||
import java.io.File;
|
||||
@@ -20,6 +19,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import net.sf.briar.BriarTestCase;
|
||||
import net.sf.briar.TestDatabaseConfig;
|
||||
import net.sf.briar.TestMessage;
|
||||
import net.sf.briar.TestUtils;
|
||||
import net.sf.briar.api.ContactId;
|
||||
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.MessageHeader;
|
||||
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.GroupId;
|
||||
import net.sf.briar.api.protocol.Message;
|
||||
@@ -53,10 +52,9 @@ public class H2DatabaseTest extends BriarTestCase {
|
||||
private final Random random = new Random();
|
||||
private final Group group;
|
||||
private final AuthorId authorId;
|
||||
private final BatchId batchId;
|
||||
private final ContactId contactId;
|
||||
private final GroupId groupId;
|
||||
private final MessageId messageId, privateMessageId;
|
||||
private final MessageId messageId, messageId1;
|
||||
private final String subject;
|
||||
private final long timestamp;
|
||||
private final int size;
|
||||
@@ -67,11 +65,10 @@ public class H2DatabaseTest extends BriarTestCase {
|
||||
public H2DatabaseTest() throws Exception {
|
||||
super();
|
||||
authorId = new AuthorId(TestUtils.getRandomId());
|
||||
batchId = new BatchId(TestUtils.getRandomId());
|
||||
contactId = new ContactId(1);
|
||||
groupId = new GroupId(TestUtils.getRandomId());
|
||||
messageId = new MessageId(TestUtils.getRandomId());
|
||||
privateMessageId = new MessageId(TestUtils.getRandomId());
|
||||
messageId1 = new MessageId(TestUtils.getRandomId());
|
||||
group = new Group(groupId, "Foo", null);
|
||||
subject = "Foo";
|
||||
timestamp = System.currentTimeMillis();
|
||||
@@ -80,7 +77,7 @@ public class H2DatabaseTest extends BriarTestCase {
|
||||
random.nextBytes(raw);
|
||||
message = new TestMessage(messageId, null, groupId, authorId, subject,
|
||||
timestamp, raw);
|
||||
privateMessage = new TestMessage(privateMessageId, null, null, null,
|
||||
privateMessage = new TestMessage(messageId1, null, null, null,
|
||||
subject, timestamp, raw);
|
||||
transportId = new TransportId(TestUtils.getRandomId());
|
||||
}
|
||||
@@ -104,9 +101,9 @@ public class H2DatabaseTest extends BriarTestCase {
|
||||
assertFalse(db.containsMessage(txn, messageId));
|
||||
db.addGroupMessage(txn, message);
|
||||
assertTrue(db.containsMessage(txn, messageId));
|
||||
assertFalse(db.containsMessage(txn, privateMessageId));
|
||||
assertFalse(db.containsMessage(txn, messageId1));
|
||||
db.addPrivateMessage(txn, privateMessage, contactId);
|
||||
assertTrue(db.containsMessage(txn, privateMessageId));
|
||||
assertTrue(db.containsMessage(txn, messageId1));
|
||||
db.commitTransaction(txn);
|
||||
db.close();
|
||||
|
||||
@@ -118,12 +115,12 @@ public class H2DatabaseTest extends BriarTestCase {
|
||||
assertTrue(db.containsMessage(txn, messageId));
|
||||
byte[] raw1 = db.getMessage(txn, messageId);
|
||||
assertArrayEquals(raw, raw1);
|
||||
assertTrue(db.containsMessage(txn, privateMessageId));
|
||||
raw1 = db.getMessage(txn, privateMessageId);
|
||||
assertTrue(db.containsMessage(txn, messageId1));
|
||||
raw1 = db.getMessage(txn, messageId1);
|
||||
assertArrayEquals(raw, raw1);
|
||||
// Delete the records
|
||||
db.removeMessage(txn, messageId);
|
||||
db.removeMessage(txn, privateMessageId);
|
||||
db.removeMessage(txn, messageId1);
|
||||
db.removeContact(txn, contactId);
|
||||
db.removeSubscription(txn, groupId);
|
||||
db.commitTransaction(txn);
|
||||
@@ -137,7 +134,7 @@ public class H2DatabaseTest extends BriarTestCase {
|
||||
db.getRemoteProperties(txn, transportId));
|
||||
assertFalse(db.containsSubscription(txn, groupId));
|
||||
assertFalse(db.containsMessage(txn, messageId));
|
||||
assertFalse(db.containsMessage(txn, privateMessageId));
|
||||
assertFalse(db.containsMessage(txn, messageId1));
|
||||
db.commitTransaction(txn);
|
||||
db.close();
|
||||
}
|
||||
@@ -216,9 +213,9 @@ public class H2DatabaseTest extends BriarTestCase {
|
||||
db.addPrivateMessage(txn, privateMessage, contactId);
|
||||
|
||||
// Removing the contact should remove the message
|
||||
assertTrue(db.containsMessage(txn, privateMessageId));
|
||||
assertTrue(db.containsMessage(txn, messageId1));
|
||||
db.removeContact(txn, contactId);
|
||||
assertFalse(db.containsMessage(txn, privateMessageId));
|
||||
assertFalse(db.containsMessage(txn, messageId1));
|
||||
|
||||
db.commitTransaction(txn);
|
||||
db.close();
|
||||
@@ -241,21 +238,21 @@ public class H2DatabaseTest extends BriarTestCase {
|
||||
assertFalse(it.hasNext());
|
||||
|
||||
// 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));
|
||||
it = db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator();
|
||||
assertTrue(it.hasNext());
|
||||
assertEquals(privateMessageId, it.next());
|
||||
assertEquals(messageId1, it.next());
|
||||
assertFalse(it.hasNext());
|
||||
|
||||
// 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));
|
||||
it = db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator();
|
||||
assertFalse(it.hasNext());
|
||||
|
||||
// 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();
|
||||
assertFalse(it.hasNext());
|
||||
|
||||
@@ -272,7 +269,7 @@ public class H2DatabaseTest extends BriarTestCase {
|
||||
// Add a contact and store a private message
|
||||
assertEquals(contactId, db.addContact(txn));
|
||||
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
|
||||
assertTrue(db.hasSendableMessages(txn, contactId));
|
||||
@@ -284,7 +281,7 @@ public class H2DatabaseTest extends BriarTestCase {
|
||||
assertTrue(db.hasSendableMessages(txn, contactId));
|
||||
it = db.getSendableMessages(txn, contactId, size).iterator();
|
||||
assertTrue(it.hasNext());
|
||||
assertEquals(privateMessageId, it.next());
|
||||
assertEquals(messageId1, it.next());
|
||||
assertFalse(it.hasNext());
|
||||
|
||||
db.commitTransaction(txn);
|
||||
@@ -508,109 +505,58 @@ public class H2DatabaseTest extends BriarTestCase {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBatchesToAck() throws Exception {
|
||||
BatchId batchId1 = new BatchId(TestUtils.getRandomId());
|
||||
public void testMessagesToAck() throws Exception {
|
||||
Database<Connection> db = open(false);
|
||||
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));
|
||||
db.addBatchToAck(txn, contactId, batchId);
|
||||
db.addBatchToAck(txn, contactId, batchId1);
|
||||
db.addMessageToAck(txn, contactId, messageId);
|
||||
db.addMessageToAck(txn, contactId, messageId1);
|
||||
|
||||
// Both batch IDs should be returned
|
||||
Collection<BatchId> acks = db.getBatchesToAck(txn, contactId, 1234);
|
||||
assertEquals(2, acks.size());
|
||||
assertTrue(acks.contains(batchId));
|
||||
assertTrue(acks.contains(batchId1));
|
||||
// Both message IDs should be returned
|
||||
Collection<MessageId> ids = Arrays.asList(messageId, messageId1);
|
||||
assertEquals(ids, db.getMessagesToAck(txn, contactId, 1234));
|
||||
|
||||
// Remove the batch IDs
|
||||
db.removeBatchesToAck(txn, contactId, acks);
|
||||
// Remove both message IDs
|
||||
db.removeMessagesToAck(txn, contactId, ids);
|
||||
|
||||
// Both batch IDs should have been removed
|
||||
acks = db.getBatchesToAck(txn, contactId, 1234);
|
||||
assertEquals(0, acks.size());
|
||||
// Both message IDs should have been removed
|
||||
assertEquals(Collections.emptyList(), db.getMessagesToAck(txn,
|
||||
contactId, 1234));
|
||||
|
||||
db.commitTransaction(txn);
|
||||
db.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDuplicateBatchesReceived() throws Exception {
|
||||
public void testDuplicateMessageReceived() throws Exception {
|
||||
Database<Connection> db = open(false);
|
||||
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));
|
||||
db.addBatchToAck(txn, contactId, batchId);
|
||||
db.addBatchToAck(txn, contactId, batchId);
|
||||
db.addMessageToAck(txn, contactId, messageId);
|
||||
db.addMessageToAck(txn, contactId, messageId);
|
||||
|
||||
// The batch ID should only be returned once
|
||||
Collection<BatchId> acks = db.getBatchesToAck(txn, contactId, 1234);
|
||||
assertEquals(1, acks.size());
|
||||
assertTrue(acks.contains(batchId));
|
||||
// The message ID should only be returned once
|
||||
Collection<MessageId> ids = db.getMessagesToAck(txn, contactId, 1234);
|
||||
assertEquals(Collections.singletonList(messageId), ids);
|
||||
|
||||
// Remove the batch ID
|
||||
db.removeBatchesToAck(txn, contactId, acks);
|
||||
// Remove the message ID
|
||||
db.removeMessagesToAck(txn, contactId,
|
||||
Collections.singletonList(messageId));
|
||||
|
||||
// The batch ID should have been removed
|
||||
acks = db.getBatchesToAck(txn, contactId, 1234);
|
||||
assertEquals(0, acks.size());
|
||||
// The message ID should have been removed
|
||||
assertEquals(Collections.emptyList(), db.getMessagesToAck(txn,
|
||||
contactId, 1234));
|
||||
|
||||
db.commitTransaction(txn);
|
||||
db.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSameBatchCannotBeSentTwice() 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 {
|
||||
public void testRemoveAckedMessage() throws Exception {
|
||||
Database<Connection> db = open(false);
|
||||
Connection txn = db.startTransaction();
|
||||
|
||||
@@ -630,15 +576,16 @@ public class H2DatabaseTest extends BriarTestCase {
|
||||
assertEquals(messageId, it.next());
|
||||
assertFalse(it.hasNext());
|
||||
db.setStatus(txn, contactId, messageId, Status.SENT);
|
||||
db.addOutstandingBatch(txn, contactId, batchId,
|
||||
db.addOutstandingMessages(txn, contactId,
|
||||
Collections.singletonList(messageId));
|
||||
|
||||
// The message should no longer be sendable
|
||||
it = db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator();
|
||||
assertFalse(it.hasNext());
|
||||
|
||||
// Pretend that the batch was acked
|
||||
db.removeAckedBatch(txn, contactId, batchId);
|
||||
// Pretend that the message was acked
|
||||
db.removeAckedMessages(txn, contactId,
|
||||
Collections.singletonList(messageId));
|
||||
|
||||
// The message still should not be sendable
|
||||
it = db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator();
|
||||
@@ -649,7 +596,7 @@ public class H2DatabaseTest extends BriarTestCase {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveLostBatch() throws Exception {
|
||||
public void testRemoveLostMessage() throws Exception {
|
||||
Database<Connection> db = open(false);
|
||||
Connection txn = db.startTransaction();
|
||||
|
||||
@@ -669,15 +616,16 @@ public class H2DatabaseTest extends BriarTestCase {
|
||||
assertEquals(messageId, it.next());
|
||||
assertFalse(it.hasNext());
|
||||
db.setStatus(txn, contactId, messageId, Status.SENT);
|
||||
db.addOutstandingBatch(txn, contactId, batchId,
|
||||
db.addOutstandingMessages(txn, contactId,
|
||||
Collections.singletonList(messageId));
|
||||
|
||||
// The message should no longer be sendable
|
||||
it = db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator();
|
||||
assertFalse(it.hasNext());
|
||||
|
||||
// Pretend that the batch was lost
|
||||
db.removeLostBatch(txn, contactId, batchId);
|
||||
// Pretend that the message was lost
|
||||
db.removeLostMessages(txn, contactId,
|
||||
Collections.singletonList(messageId));
|
||||
|
||||
// The message should be sendable again
|
||||
it = db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator();
|
||||
@@ -689,77 +637,6 @@ public class H2DatabaseTest extends BriarTestCase {
|
||||
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
|
||||
public void testGetMessagesByAuthor() throws Exception {
|
||||
AuthorId authorId1 = new AuthorId(TestUtils.getRandomId());
|
||||
@@ -1426,12 +1303,12 @@ public class H2DatabaseTest extends BriarTestCase {
|
||||
|
||||
// A message with a private parent should return null
|
||||
MessageId childId = new MessageId(TestUtils.getRandomId());
|
||||
Message child = new TestMessage(childId, privateMessageId, groupId,
|
||||
Message child = new TestMessage(childId, messageId1, groupId,
|
||||
null, subject, timestamp, raw);
|
||||
db.addGroupMessage(txn, child);
|
||||
db.addPrivateMessage(txn, privateMessage, contactId);
|
||||
assertTrue(db.containsMessage(txn, childId));
|
||||
assertTrue(db.containsMessage(txn, privateMessageId));
|
||||
assertTrue(db.containsMessage(txn, messageId1));
|
||||
assertNull(db.getGroupMessageParent(txn, childId));
|
||||
|
||||
db.commitTransaction(txn);
|
||||
@@ -1477,7 +1354,7 @@ public class H2DatabaseTest extends BriarTestCase {
|
||||
int bodyLength = raw.length - 20;
|
||||
Message message1 = new TestMessage(messageId, null, groupId, null,
|
||||
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);
|
||||
db.addGroupMessage(txn, message1);
|
||||
db.addPrivateMessage(txn, privateMessage1, contactId);
|
||||
@@ -1492,12 +1369,12 @@ public class H2DatabaseTest extends BriarTestCase {
|
||||
|
||||
// Retrieve the raw messages
|
||||
assertArrayEquals(raw, db.getMessage(txn, messageId));
|
||||
assertArrayEquals(raw, db.getMessage(txn, privateMessageId));
|
||||
assertArrayEquals(raw, db.getMessage(txn, messageId1));
|
||||
|
||||
// Retrieve the message bodies
|
||||
byte[] body = db.getMessageBody(txn, messageId);
|
||||
assertArrayEquals(expectedBody, body);
|
||||
byte[] body1 = db.getMessageBody(txn, privateMessageId);
|
||||
byte[] body1 = db.getMessageBody(txn, messageId1);
|
||||
assertArrayEquals(expectedBody1, body1);
|
||||
|
||||
db.commitTransaction(txn);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,6 @@ import java.io.ByteArrayOutputStream;
|
||||
import java.security.PrivateKey;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
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.Author;
|
||||
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.GroupFactory;
|
||||
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.ProtocolWriter;
|
||||
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.TransportId;
|
||||
import net.sf.briar.api.protocol.TransportUpdate;
|
||||
@@ -69,24 +66,24 @@ public class ConstantsTest extends BriarTestCase {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBatchesFitIntoLargeAck() throws Exception {
|
||||
testBatchesFitIntoAck(MAX_PACKET_LENGTH);
|
||||
public void testMessageIdsFitIntoLargeAck() throws Exception {
|
||||
testMessageIdsFitIntoAck(MAX_PACKET_LENGTH);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBatchesFitIntoSmallAck() throws Exception {
|
||||
testBatchesFitIntoAck(1000);
|
||||
public void testMessageIdsFitIntoSmallAck() throws Exception {
|
||||
testMessageIdsFitIntoAck(1000);
|
||||
}
|
||||
|
||||
private void testBatchesFitIntoAck(int length) throws Exception {
|
||||
// Create an ack with as many batch IDs as possible
|
||||
private void testMessageIdsFitIntoAck(int length) throws Exception {
|
||||
// Create an ack with as many message IDs as possible
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream(length);
|
||||
ProtocolWriter writer = protocolWriterFactory.createProtocolWriter(out,
|
||||
true);
|
||||
int maxBatches = writer.getMaxBatchesForAck(length);
|
||||
Collection<BatchId> acked = new ArrayList<BatchId>();
|
||||
for(int i = 0; i < maxBatches; i++) {
|
||||
acked.add(new BatchId(TestUtils.getRandomId()));
|
||||
int maxMessages = writer.getMaxMessagesForAck(length);
|
||||
Collection<MessageId> acked = new ArrayList<MessageId>();
|
||||
for(int i = 0; i < maxMessages; i++) {
|
||||
acked.add(new MessageId(TestUtils.getRandomId()));
|
||||
}
|
||||
Ack a = packetFactory.createAck(acked);
|
||||
writer.writeAck(a);
|
||||
@@ -95,7 +92,7 @@ public class ConstantsTest extends BriarTestCase {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMessageFitsIntoBatch() throws Exception {
|
||||
public void testMessageFitsIntoPacket() throws Exception {
|
||||
// Create a maximum-length group
|
||||
String groupName = createRandomString(MAX_GROUP_NAME_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];
|
||||
Message message = messageFactory.createMessage(null, group,
|
||||
groupPrivate, author, authorPrivate, subject, body);
|
||||
// Add the message to a batch
|
||||
ByteArrayOutputStream out =
|
||||
new ByteArrayOutputStream(MAX_PACKET_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
|
||||
// Check the size of the serialised message
|
||||
int length = message.getSerialised().length;
|
||||
assertTrue(length > UniqueId.LENGTH + MAX_GROUP_NAME_LENGTH
|
||||
+ MAX_PUBLIC_KEY_LENGTH + MAX_AUTHOR_NAME_LENGTH
|
||||
+ MAX_PUBLIC_KEY_LENGTH + MAX_BODY_LENGTH);
|
||||
assertTrue(out.size() <= MAX_PACKET_LENGTH);
|
||||
assertTrue(length <= MAX_PACKET_LENGTH);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMessagesFitIntoLargeOffer() throws Exception {
|
||||
testMessagesFitIntoOffer(MAX_PACKET_LENGTH);
|
||||
public void testMessageIdsFitIntoLargeOffer() throws Exception {
|
||||
testMessageIdsFitIntoOffer(MAX_PACKET_LENGTH);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMessagesFitIntoSmallOffer() throws Exception {
|
||||
testMessagesFitIntoOffer(1000);
|
||||
public void testMessageIdsFitIntoSmallOffer() throws Exception {
|
||||
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
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream(length);
|
||||
ProtocolWriter writer = protocolWriterFactory.createProtocolWriter(out,
|
||||
@@ -170,7 +160,7 @@ public class ConstantsTest extends BriarTestCase {
|
||||
}
|
||||
// Add the transports to an update
|
||||
ByteArrayOutputStream out =
|
||||
new ByteArrayOutputStream(MAX_PACKET_LENGTH);
|
||||
new ByteArrayOutputStream(MAX_PACKET_LENGTH);
|
||||
ProtocolWriter writer = protocolWriterFactory.createProtocolWriter(out,
|
||||
true);
|
||||
TransportUpdate t = packetFactory.createTransportUpdate(transports,
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -17,9 +17,8 @@ import net.sf.briar.api.ContactId;
|
||||
import net.sf.briar.api.db.DatabaseComponent;
|
||||
import net.sf.briar.api.db.DatabaseExecutor;
|
||||
import net.sf.briar.api.protocol.Ack;
|
||||
import net.sf.briar.api.protocol.BatchId;
|
||||
import net.sf.briar.api.protocol.MessageId;
|
||||
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.UniqueId;
|
||||
import net.sf.briar.api.transport.ConnectionContext;
|
||||
@@ -51,6 +50,7 @@ public class OutgoingSimplexConnectionTest extends BriarTestCase {
|
||||
private final ConnectionWriterFactory connFactory;
|
||||
private final ProtocolWriterFactory protoFactory;
|
||||
private final ContactId contactId;
|
||||
private final MessageId messageId;
|
||||
private final TransportId transportId;
|
||||
private final byte[] secret;
|
||||
|
||||
@@ -75,6 +75,7 @@ public class OutgoingSimplexConnectionTest extends BriarTestCase {
|
||||
connFactory = i.getInstance(ConnectionWriterFactory.class);
|
||||
protoFactory = i.getInstance(ProtocolWriterFactory.class);
|
||||
contactId = new ContactId(234);
|
||||
messageId = new MessageId(TestUtils.getRandomId());
|
||||
transportId = new TransportId(TestUtils.getRandomId());
|
||||
secret = new byte[32];
|
||||
}
|
||||
@@ -115,7 +116,7 @@ public class OutgoingSimplexConnectionTest extends BriarTestCase {
|
||||
// No acks to send
|
||||
oneOf(db).generateAck(with(contactId), with(any(int.class)));
|
||||
will(returnValue(null));
|
||||
// No batches to send
|
||||
// No messages to send
|
||||
oneOf(db).generateBatch(with(contactId), with(any(int.class)));
|
||||
will(returnValue(null));
|
||||
}});
|
||||
@@ -138,9 +139,7 @@ public class OutgoingSimplexConnectionTest extends BriarTestCase {
|
||||
OutgoingSimplexConnection connection = new OutgoingSimplexConnection(db,
|
||||
connRegistry, connFactory, protoFactory, ctx, transport);
|
||||
final Ack ack = context.mock(Ack.class);
|
||||
final BatchId batchId = new BatchId(TestUtils.getRandomId());
|
||||
final RawBatch batch = context.mock(RawBatch.class);
|
||||
final byte[] message = new byte[1234];
|
||||
final byte[] raw = new byte[1234];
|
||||
context.checking(new Expectations() {{
|
||||
// No transports to send
|
||||
oneOf(db).generateTransportUpdate(contactId);
|
||||
@@ -151,24 +150,22 @@ public class OutgoingSimplexConnectionTest extends BriarTestCase {
|
||||
// One ack to send
|
||||
oneOf(db).generateAck(with(contactId), with(any(int.class)));
|
||||
will(returnValue(ack));
|
||||
oneOf(ack).getBatchIds();
|
||||
will(returnValue(Collections.singletonList(batchId)));
|
||||
oneOf(ack).getMessageIds();
|
||||
will(returnValue(Collections.singletonList(messageId)));
|
||||
// No more acks
|
||||
oneOf(db).generateAck(with(contactId), with(any(int.class)));
|
||||
will(returnValue(null));
|
||||
// One batch to send
|
||||
// One message to send
|
||||
oneOf(db).generateBatch(with(contactId), with(any(int.class)));
|
||||
will(returnValue(batch));
|
||||
oneOf(batch).getMessages();
|
||||
will(returnValue(Collections.singletonList(message)));
|
||||
// No more batches
|
||||
will(returnValue(Collections.singletonList(raw)));
|
||||
// No more messages
|
||||
oneOf(db).generateBatch(with(contactId), with(any(int.class)));
|
||||
will(returnValue(null));
|
||||
}});
|
||||
connection.write();
|
||||
// Something should have been written
|
||||
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
|
||||
assertTrue(transport.getDisposed());
|
||||
assertFalse(transport.getException());
|
||||
|
||||
@@ -17,9 +17,10 @@ import net.sf.briar.api.crypto.KeyManager;
|
||||
import net.sf.briar.api.db.DatabaseComponent;
|
||||
import net.sf.briar.api.db.event.DatabaseEvent;
|
||||
import net.sf.briar.api.db.event.DatabaseListener;
|
||||
import net.sf.briar.api.db.event.MessagesAddedEvent;
|
||||
import net.sf.briar.api.db.event.MessageAddedEvent;
|
||||
import net.sf.briar.api.protocol.Message;
|
||||
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.ProtocolWriterFactory;
|
||||
import net.sf.briar.api.protocol.Transport;
|
||||
@@ -181,6 +182,8 @@ public class SimplexProtocolIntegrationTest extends BriarTestCase {
|
||||
ConnectionContext ctx = rec.acceptConnection(transportId, tag);
|
||||
assertNotNull(ctx);
|
||||
// Create an incoming simplex connection
|
||||
MessageVerifier messageVerifier =
|
||||
bob.getInstance(MessageVerifier.class);
|
||||
ConnectionRegistry connRegistry =
|
||||
bob.getInstance(ConnectionRegistry.class);
|
||||
ConnectionReaderFactory connFactory =
|
||||
@@ -190,8 +193,9 @@ public class SimplexProtocolIntegrationTest extends BriarTestCase {
|
||||
TestSimplexTransportReader transport =
|
||||
new TestSimplexTransportReader(in);
|
||||
IncomingSimplexConnection simplex = new IncomingSimplexConnection(
|
||||
new ImmediateExecutor(), new ImmediateExecutor(), db,
|
||||
connRegistry, connFactory, protoFactory, ctx, transport);
|
||||
new ImmediateExecutor(), new ImmediateExecutor(),
|
||||
messageVerifier, db, connRegistry, connFactory, protoFactory,
|
||||
ctx, transport);
|
||||
// No messages should have been added yet
|
||||
assertFalse(listener.messagesAdded);
|
||||
// Read whatever needs to be read
|
||||
@@ -216,8 +220,7 @@ public class SimplexProtocolIntegrationTest extends BriarTestCase {
|
||||
private boolean messagesAdded = false;
|
||||
|
||||
public void eventOccurred(DatabaseEvent e) {
|
||||
if(e instanceof MessagesAddedEvent)
|
||||
messagesAdded = true;
|
||||
if(e instanceof MessageAddedEvent) messagesAdded = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import org.jmock.Expectations;
|
||||
import org.jmock.Mockery;
|
||||
import org.junit.Test;
|
||||
|
||||
|
||||
public class ConnectionWriterImplTest extends BriarTestCase {
|
||||
|
||||
private static final int FRAME_LENGTH = 1024;
|
||||
|
||||
@@ -18,19 +18,12 @@ import net.sf.briar.api.crypto.CryptoComponent;
|
||||
import net.sf.briar.api.crypto.ErasableKey;
|
||||
import net.sf.briar.api.protocol.TransportId;
|
||||
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.ConnectionWriterFactory;
|
||||
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 com.google.inject.AbstractModule;
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
@@ -93,7 +86,7 @@ public class TransportIntegrationTest extends BriarTestCase {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
FrameWriter encryptionOut = new OutgoingEncryptionLayer(out,
|
||||
Long.MAX_VALUE, frameCipher, frameCopy, FRAME_LENGTH);
|
||||
ConnectionWriter writer = new ConnectionWriterImpl(encryptionOut,
|
||||
ConnectionWriterImpl writer = new ConnectionWriterImpl(encryptionOut,
|
||||
FRAME_LENGTH);
|
||||
OutputStream out1 = writer.getOutputStream();
|
||||
out1.write(frame);
|
||||
@@ -106,7 +99,7 @@ public class TransportIntegrationTest extends BriarTestCase {
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(output);
|
||||
FrameReader encryptionIn = new IncomingEncryptionLayer(in, frameCipher,
|
||||
frameKey, FRAME_LENGTH);
|
||||
ConnectionReader reader = new ConnectionReaderImpl(encryptionIn,
|
||||
ConnectionReaderImpl reader = new ConnectionReaderImpl(encryptionIn,
|
||||
FRAME_LENGTH);
|
||||
InputStream in1 = reader.getInputStream();
|
||||
byte[] recovered = new byte[frame.length];
|
||||
|
||||
Reference in New Issue
Block a user