mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-18 21:59:54 +01:00
Rewrote the bundle reading and writing code to eliminate copying. Signatures and digests are now calculated on the fly as the data is read or written. This is a little bit tricky in the case of reading because ReaderImpl uses a lookahead byte, so the signature and message digest need to lag one byte behind.
This commit is contained in:
@@ -8,12 +8,6 @@ public interface Batch {
|
|||||||
/** Returns the batch's unique identifier. */
|
/** Returns the batch's unique identifier. */
|
||||||
BatchId getId();
|
BatchId getId();
|
||||||
|
|
||||||
/** Returns the size of the serialised batch in bytes. */
|
|
||||||
long getSize();
|
|
||||||
|
|
||||||
/** Returns the messages contained in the batch. */
|
/** Returns the messages contained in the batch. */
|
||||||
Iterable<Message> getMessages();
|
Iterable<Message> getMessages();
|
||||||
|
|
||||||
/** Returns the sender's signature over the contents of the batch. */
|
|
||||||
byte[] getSignature();
|
|
||||||
}
|
}
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
package net.sf.briar.api.protocol;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.security.GeneralSecurityException;
|
|
||||||
|
|
||||||
public interface BatchBuilder {
|
|
||||||
|
|
||||||
/** Adds a message to the batch. */
|
|
||||||
void addMessage(Message m);
|
|
||||||
|
|
||||||
/** Sets the sender's signature over the contents of the batch. */
|
|
||||||
void setSignature(byte[] sig);
|
|
||||||
|
|
||||||
/** Builds and returns the batch. */
|
|
||||||
Batch build() throws IOException, GeneralSecurityException;
|
|
||||||
}
|
|
||||||
@@ -9,9 +9,6 @@ import java.security.GeneralSecurityException;
|
|||||||
*/
|
*/
|
||||||
public interface BundleReader {
|
public interface BundleReader {
|
||||||
|
|
||||||
/** Returns the size of the serialised bundle in bytes. */
|
|
||||||
long getSize() throws IOException;
|
|
||||||
|
|
||||||
/** Returns the bundle's header. */
|
/** Returns the bundle's header. */
|
||||||
Header getHeader() throws IOException, GeneralSecurityException;
|
Header getHeader() throws IOException, GeneralSecurityException;
|
||||||
|
|
||||||
@@ -21,5 +18,5 @@ public interface BundleReader {
|
|||||||
Batch getNextBatch() throws IOException, GeneralSecurityException;
|
Batch getNextBatch() throws IOException, GeneralSecurityException;
|
||||||
|
|
||||||
/** Finishes reading the bundle. */
|
/** Finishes reading the bundle. */
|
||||||
void close() throws IOException;
|
void finish() throws IOException;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package net.sf.briar.api.protocol;
|
package net.sf.briar.api.protocol;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.security.GeneralSecurityException;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An interface for writing a bundle of acknowledgements, subscriptions,
|
* An interface for writing a bundle of acknowledgements, subscriptions,
|
||||||
@@ -8,15 +10,18 @@ import java.io.IOException;
|
|||||||
*/
|
*/
|
||||||
public interface BundleWriter {
|
public interface BundleWriter {
|
||||||
|
|
||||||
/** Returns the bundle's capacity in bytes. */
|
/** Returns the bundle's remaining capacity in bytes. */
|
||||||
long getCapacity() throws IOException;
|
long getRemainingCapacity() throws IOException;
|
||||||
|
|
||||||
/** Adds a header to the bundle. */
|
/** Adds a header to the bundle and returns its identifier. */
|
||||||
void addHeader(Header h) throws IOException;
|
BundleId addHeader(Iterable<BatchId> acks, Iterable<GroupId> subs,
|
||||||
|
Map<String, String> transports) throws IOException,
|
||||||
|
GeneralSecurityException;
|
||||||
|
|
||||||
/** Adds a batch of messages to the bundle. */
|
/** Adds a batch to the bundle and returns its identifier. */
|
||||||
void addBatch(Batch b) throws IOException;
|
BatchId addBatch(Iterable<Message> messages) throws IOException,
|
||||||
|
GeneralSecurityException;
|
||||||
|
|
||||||
/** Finishes writing the bundle. */
|
/** Finishes writing the bundle. */
|
||||||
void close() throws IOException;
|
void finish() throws IOException;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,9 +11,6 @@ public interface Header {
|
|||||||
// FIXME: Remove BundleId when refactoring is complete
|
// FIXME: Remove BundleId when refactoring is complete
|
||||||
BundleId getId();
|
BundleId getId();
|
||||||
|
|
||||||
/** Returns the size of the serialised header in bytes. */
|
|
||||||
long getSize();
|
|
||||||
|
|
||||||
/** Returns the acknowledgements contained in the header. */
|
/** Returns the acknowledgements contained in the header. */
|
||||||
Set<BatchId> getAcks();
|
Set<BatchId> getAcks();
|
||||||
|
|
||||||
@@ -22,7 +19,4 @@ public interface Header {
|
|||||||
|
|
||||||
/** Returns the transport details contained in the header. */
|
/** Returns the transport details contained in the header. */
|
||||||
Map<String, String> getTransports();
|
Map<String, String> getTransports();
|
||||||
|
|
||||||
/** Returns the sender's signature over the contents of the header. */
|
|
||||||
byte[] getSignature();
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
package net.sf.briar.api.protocol;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.security.GeneralSecurityException;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public interface HeaderBuilder {
|
|
||||||
|
|
||||||
/** Adds acknowledgements to the header. */
|
|
||||||
void addAcks(Iterable<BatchId> acks);
|
|
||||||
|
|
||||||
/** Adds subscriptions to the header. */
|
|
||||||
void addSubscriptions(Iterable<GroupId> subs);
|
|
||||||
|
|
||||||
/** Adds transport details to the header. */
|
|
||||||
void addTransports(Map<String, String> transports);
|
|
||||||
|
|
||||||
/** Sets the sender's signature over the contents of the header. */
|
|
||||||
void setSignature(byte[] sig);
|
|
||||||
|
|
||||||
/** Builds and returns the header. */
|
|
||||||
Header build() throws IOException, GeneralSecurityException;
|
|
||||||
}
|
|
||||||
@@ -3,5 +3,5 @@ package net.sf.briar.api.protocol;
|
|||||||
public interface MessageFactory {
|
public interface MessageFactory {
|
||||||
|
|
||||||
Message createMessage(MessageId id, MessageId parent, GroupId group,
|
Message createMessage(MessageId id, MessageId parent, GroupId group,
|
||||||
AuthorId author, long timestamp, byte[] body);
|
AuthorId author, long timestamp, byte[] raw);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
package net.sf.briar.api.serial;
|
package net.sf.briar.api.serial;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generic interface for any object that knows how to serialise itself as a
|
||||||
|
* raw byte array.
|
||||||
|
*/
|
||||||
public interface Raw {
|
public interface Raw {
|
||||||
|
|
||||||
byte[] getBytes();
|
byte[] getBytes();
|
||||||
|
|||||||
@@ -9,13 +9,9 @@ import net.sf.briar.api.db.DatabaseComponent;
|
|||||||
import net.sf.briar.api.db.DbException;
|
import net.sf.briar.api.db.DbException;
|
||||||
import net.sf.briar.api.db.Status;
|
import net.sf.briar.api.db.Status;
|
||||||
import net.sf.briar.api.protocol.AuthorId;
|
import net.sf.briar.api.protocol.AuthorId;
|
||||||
import net.sf.briar.api.protocol.BatchBuilder;
|
|
||||||
import net.sf.briar.api.protocol.HeaderBuilder;
|
|
||||||
import net.sf.briar.api.protocol.Message;
|
import net.sf.briar.api.protocol.Message;
|
||||||
import net.sf.briar.api.protocol.MessageId;
|
import net.sf.briar.api.protocol.MessageId;
|
||||||
|
|
||||||
import com.google.inject.Provider;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract superclass containing code shared by ReadWriteLockDatabaseComponent
|
* Abstract superclass containing code shared by ReadWriteLockDatabaseComponent
|
||||||
* and SynchronizedDatabaseComponent.
|
* and SynchronizedDatabaseComponent.
|
||||||
@@ -28,8 +24,6 @@ DatabaseCleaner.Callback {
|
|||||||
|
|
||||||
protected final Database<Txn> db;
|
protected final Database<Txn> db;
|
||||||
protected final DatabaseCleaner cleaner;
|
protected final DatabaseCleaner cleaner;
|
||||||
protected final Provider<HeaderBuilder> headerBuilderProvider;
|
|
||||||
protected final Provider<BatchBuilder> batchBuilderProvider;
|
|
||||||
|
|
||||||
private final Object spaceLock = new Object();
|
private final Object spaceLock = new Object();
|
||||||
private final Object writeLock = new Object();
|
private final Object writeLock = new Object();
|
||||||
@@ -37,13 +31,9 @@ DatabaseCleaner.Callback {
|
|||||||
private long timeOfLastCheck = 0L; // Locking: spaceLock
|
private long timeOfLastCheck = 0L; // Locking: spaceLock
|
||||||
private volatile boolean writesAllowed = true;
|
private volatile boolean writesAllowed = true;
|
||||||
|
|
||||||
DatabaseComponentImpl(Database<Txn> db, DatabaseCleaner cleaner,
|
DatabaseComponentImpl(Database<Txn> db, DatabaseCleaner cleaner) {
|
||||||
Provider<HeaderBuilder> headerBuilderProvider,
|
|
||||||
Provider<BatchBuilder> batchBuilderProvider) {
|
|
||||||
this.db = db;
|
this.db = db;
|
||||||
this.cleaner = cleaner;
|
this.cleaner = cleaner;
|
||||||
this.headerBuilderProvider = headerBuilderProvider;
|
|
||||||
this.batchBuilderProvider = batchBuilderProvider;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void open(boolean resume) throws DbException {
|
public void open(boolean resume) throws DbException {
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
+ " authorId XXXX NOT NULL,"
|
+ " authorId XXXX NOT NULL,"
|
||||||
+ " timestamp BIGINT NOT NULL,"
|
+ " timestamp BIGINT NOT NULL,"
|
||||||
+ " size INT NOT NULL,"
|
+ " size INT NOT NULL,"
|
||||||
+ " body BLOB NOT NULL,"
|
+ " raw BLOB NOT NULL,"
|
||||||
+ " sendability INT NOT NULL,"
|
+ " sendability INT NOT NULL,"
|
||||||
+ " PRIMARY KEY (messageId),"
|
+ " PRIMARY KEY (messageId),"
|
||||||
+ " FOREIGN KEY (groupId) REFERENCES localSubscriptions (groupId)"
|
+ " FOREIGN KEY (groupId) REFERENCES localSubscriptions (groupId)"
|
||||||
@@ -458,7 +458,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
try {
|
try {
|
||||||
String sql = "INSERT INTO messages"
|
String sql = "INSERT INTO messages"
|
||||||
+ " (messageId, parentId, groupId, authorId, timestamp, size,"
|
+ " (messageId, parentId, groupId, authorId, timestamp, size,"
|
||||||
+ " body, sendability)"
|
+ " raw, sendability)"
|
||||||
+ " VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
|
+ " VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
|
||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
ps.setBytes(1, m.getId().getBytes());
|
ps.setBytes(1, m.getId().getBytes());
|
||||||
@@ -834,7 +834,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ResultSet rs = null;
|
ResultSet rs = null;
|
||||||
try {
|
try {
|
||||||
String sql =
|
String sql =
|
||||||
"SELECT parentId, groupId, authorId, timestamp, size, body"
|
"SELECT parentId, groupId, authorId, timestamp, size, raw"
|
||||||
+ " FROM messages WHERE messageId = ?";
|
+ " FROM messages WHERE messageId = ?";
|
||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
ps.setBytes(1, m.getBytes());
|
ps.setBytes(1, m.getBytes());
|
||||||
@@ -847,14 +847,14 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
long timestamp = rs.getLong(4);
|
long timestamp = rs.getLong(4);
|
||||||
int size = rs.getInt(5);
|
int size = rs.getInt(5);
|
||||||
Blob b = rs.getBlob(6);
|
Blob b = rs.getBlob(6);
|
||||||
byte[] body = b.getBytes(1, size);
|
byte[] raw = b.getBytes(1, size);
|
||||||
assert body.length == size;
|
assert raw.length == size;
|
||||||
boolean more = rs.next();
|
boolean more = rs.next();
|
||||||
assert !more;
|
assert !more;
|
||||||
rs.close();
|
rs.close();
|
||||||
ps.close();
|
ps.close();
|
||||||
return messageFactory.createMessage(m, parent, group, author,
|
return messageFactory.createMessage(m, parent, group, author,
|
||||||
timestamp, body);
|
timestamp, raw);
|
||||||
} catch(SQLException e) {
|
} catch(SQLException e) {
|
||||||
tryToClose(rs);
|
tryToClose(rs);
|
||||||
tryToClose(ps);
|
tryToClose(ps);
|
||||||
|
|||||||
@@ -2,9 +2,10 @@ package net.sf.briar.db;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
import java.security.SignatureException;
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||||
@@ -17,19 +18,16 @@ import net.sf.briar.api.db.DbException;
|
|||||||
import net.sf.briar.api.db.NoSuchContactException;
|
import net.sf.briar.api.db.NoSuchContactException;
|
||||||
import net.sf.briar.api.protocol.AuthorId;
|
import net.sf.briar.api.protocol.AuthorId;
|
||||||
import net.sf.briar.api.protocol.Batch;
|
import net.sf.briar.api.protocol.Batch;
|
||||||
import net.sf.briar.api.protocol.BatchBuilder;
|
|
||||||
import net.sf.briar.api.protocol.BatchId;
|
import net.sf.briar.api.protocol.BatchId;
|
||||||
import net.sf.briar.api.protocol.BundleId;
|
import net.sf.briar.api.protocol.BundleId;
|
||||||
import net.sf.briar.api.protocol.BundleReader;
|
import net.sf.briar.api.protocol.BundleReader;
|
||||||
import net.sf.briar.api.protocol.BundleWriter;
|
import net.sf.briar.api.protocol.BundleWriter;
|
||||||
import net.sf.briar.api.protocol.GroupId;
|
import net.sf.briar.api.protocol.GroupId;
|
||||||
import net.sf.briar.api.protocol.Header;
|
import net.sf.briar.api.protocol.Header;
|
||||||
import net.sf.briar.api.protocol.HeaderBuilder;
|
|
||||||
import net.sf.briar.api.protocol.Message;
|
import net.sf.briar.api.protocol.Message;
|
||||||
import net.sf.briar.api.protocol.MessageId;
|
import net.sf.briar.api.protocol.MessageId;
|
||||||
|
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Provider;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An implementation of DatabaseComponent using reentrant read-write locks.
|
* An implementation of DatabaseComponent using reentrant read-write locks.
|
||||||
@@ -59,10 +57,8 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
|||||||
new ReentrantReadWriteLock(true);
|
new ReentrantReadWriteLock(true);
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
ReadWriteLockDatabaseComponent(Database<Txn> db, DatabaseCleaner cleaner,
|
ReadWriteLockDatabaseComponent(Database<Txn> db, DatabaseCleaner cleaner) {
|
||||||
Provider<HeaderBuilder> headerBuilderProvider,
|
super(db, cleaner);
|
||||||
Provider<BatchBuilder> batchBuilderProvider) {
|
|
||||||
super(db, cleaner, headerBuilderProvider, batchBuilderProvider);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void close() throws DbException {
|
public void close() throws DbException {
|
||||||
@@ -195,18 +191,18 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
|||||||
public void generateBundle(ContactId c, BundleWriter b) throws DbException,
|
public void generateBundle(ContactId c, BundleWriter b) throws DbException,
|
||||||
IOException, GeneralSecurityException {
|
IOException, GeneralSecurityException {
|
||||||
if(LOG.isLoggable(Level.FINE)) LOG.fine("Generating bundle for " + c);
|
if(LOG.isLoggable(Level.FINE)) LOG.fine("Generating bundle for " + c);
|
||||||
HeaderBuilder h;
|
Set<BatchId> acks;
|
||||||
|
Set<GroupId> subs;
|
||||||
|
Map<String, String> transports;
|
||||||
// Add acks
|
// Add acks
|
||||||
contactLock.readLock().lock();
|
contactLock.readLock().lock();
|
||||||
try {
|
try {
|
||||||
if(!containsContact(c)) throw new NoSuchContactException();
|
if(!containsContact(c)) throw new NoSuchContactException();
|
||||||
h = headerBuilderProvider.get();
|
|
||||||
messageStatusLock.writeLock().lock();
|
messageStatusLock.writeLock().lock();
|
||||||
try {
|
try {
|
||||||
Txn txn = db.startTransaction();
|
Txn txn = db.startTransaction();
|
||||||
try {
|
try {
|
||||||
Set<BatchId> acks = db.removeBatchesToAck(txn, c);
|
acks = db.removeBatchesToAck(txn, c);
|
||||||
h.addAcks(acks);
|
|
||||||
if(LOG.isLoggable(Level.FINE))
|
if(LOG.isLoggable(Level.FINE))
|
||||||
LOG.fine("Added " + acks.size() + " acks");
|
LOG.fine("Added " + acks.size() + " acks");
|
||||||
db.commitTransaction(txn);
|
db.commitTransaction(txn);
|
||||||
@@ -228,8 +224,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
|||||||
try {
|
try {
|
||||||
Txn txn = db.startTransaction();
|
Txn txn = db.startTransaction();
|
||||||
try {
|
try {
|
||||||
Set<GroupId> subs = db.getSubscriptions(txn);
|
subs = db.getSubscriptions(txn);
|
||||||
h.addSubscriptions(subs);
|
|
||||||
if(LOG.isLoggable(Level.FINE))
|
if(LOG.isLoggable(Level.FINE))
|
||||||
LOG.fine("Added " + subs.size() + " subscriptions");
|
LOG.fine("Added " + subs.size() + " subscriptions");
|
||||||
db.commitTransaction(txn);
|
db.commitTransaction(txn);
|
||||||
@@ -251,8 +246,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
|||||||
try {
|
try {
|
||||||
Txn txn = db.startTransaction();
|
Txn txn = db.startTransaction();
|
||||||
try {
|
try {
|
||||||
Map<String, String> transports = db.getTransports(txn);
|
transports = db.getTransports(txn);
|
||||||
h.addTransports(transports);
|
|
||||||
if(LOG.isLoggable(Level.FINE))
|
if(LOG.isLoggable(Level.FINE))
|
||||||
LOG.fine("Added " + transports.size() + " transports");
|
LOG.fine("Added " + transports.size() + " transports");
|
||||||
db.commitTransaction(txn);
|
db.commitTransaction(txn);
|
||||||
@@ -266,28 +260,16 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
|||||||
} finally {
|
} finally {
|
||||||
contactLock.readLock().unlock();
|
contactLock.readLock().unlock();
|
||||||
}
|
}
|
||||||
// Sign the header and add it to the bundle
|
// Add the header to the bundle
|
||||||
Header header = h.build();
|
b.addHeader(acks, subs, transports);
|
||||||
long capacity = b.getCapacity();
|
|
||||||
capacity -= header.getSize();
|
|
||||||
b.addHeader(header);
|
|
||||||
// Add as many messages as possible to the bundle
|
// Add as many messages as possible to the bundle
|
||||||
while(true) {
|
while(fillBatch(c, b));
|
||||||
Batch batch = fillBatch(c, capacity);
|
b.finish();
|
||||||
if(batch == null) break; // No more messages to send
|
|
||||||
b.addBatch(batch);
|
|
||||||
long size = batch.getSize();
|
|
||||||
capacity -= size;
|
|
||||||
// If the batch is less than half full, stop trying - there may be
|
|
||||||
// more messages trickling in but we can't wait forever
|
|
||||||
if(size * 2 < Batch.MAX_SIZE) break;
|
|
||||||
}
|
|
||||||
b.close();
|
|
||||||
if(LOG.isLoggable(Level.FINE)) LOG.fine("Bundle generated");
|
if(LOG.isLoggable(Level.FINE)) LOG.fine("Bundle generated");
|
||||||
System.gc();
|
System.gc();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Batch fillBatch(ContactId c, long capacity) throws DbException,
|
private boolean fillBatch(ContactId c, BundleWriter b) throws DbException,
|
||||||
IOException, GeneralSecurityException {
|
IOException, GeneralSecurityException {
|
||||||
contactLock.readLock().lock();
|
contactLock.readLock().lock();
|
||||||
try {
|
try {
|
||||||
@@ -295,31 +277,38 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
|||||||
messageLock.readLock().lock();
|
messageLock.readLock().lock();
|
||||||
try {
|
try {
|
||||||
Set<MessageId> sent;
|
Set<MessageId> sent;
|
||||||
Batch batch;
|
int bytesSent = 0;
|
||||||
|
BatchId batchId;
|
||||||
messageStatusLock.readLock().lock();
|
messageStatusLock.readLock().lock();
|
||||||
try {
|
try {
|
||||||
Txn txn = db.startTransaction();
|
Txn txn = db.startTransaction();
|
||||||
try {
|
try {
|
||||||
capacity = Math.min(capacity, Batch.MAX_SIZE);
|
long capacity = Math.min(b.getRemainingCapacity(),
|
||||||
|
Batch.MAX_SIZE);
|
||||||
Iterator<MessageId> it =
|
Iterator<MessageId> it =
|
||||||
db.getSendableMessages(txn, c, capacity).iterator();
|
db.getSendableMessages(txn, c, capacity).iterator();
|
||||||
if(!it.hasNext()) {
|
if(!it.hasNext()) {
|
||||||
db.commitTransaction(txn);
|
db.commitTransaction(txn);
|
||||||
return null; // No more messages to send
|
return false; // No more messages to send
|
||||||
}
|
}
|
||||||
sent = new HashSet<MessageId>();
|
sent = new HashSet<MessageId>();
|
||||||
BatchBuilder b = batchBuilderProvider.get();
|
List<Message> messages = new ArrayList<Message>();
|
||||||
while(it.hasNext()) {
|
while(it.hasNext()) {
|
||||||
MessageId m = it.next();
|
MessageId m = it.next();
|
||||||
b.addMessage(db.getMessage(txn, m));
|
Message message = db.getMessage(txn, m);
|
||||||
|
bytesSent += message.getSize();
|
||||||
|
messages.add(message);
|
||||||
sent.add(m);
|
sent.add(m);
|
||||||
}
|
}
|
||||||
batch = b.build();
|
batchId = b.addBatch(messages);
|
||||||
db.commitTransaction(txn);
|
db.commitTransaction(txn);
|
||||||
} catch(DbException e) {
|
} catch(DbException e) {
|
||||||
db.abortTransaction(txn);
|
db.abortTransaction(txn);
|
||||||
throw e;
|
throw e;
|
||||||
} catch(SignatureException e) {
|
} catch(IOException e) {
|
||||||
|
db.abortTransaction(txn);
|
||||||
|
throw e;
|
||||||
|
} catch(GeneralSecurityException e) {
|
||||||
db.abortTransaction(txn);
|
db.abortTransaction(txn);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
@@ -332,9 +321,10 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
|||||||
Txn txn = db.startTransaction();
|
Txn txn = db.startTransaction();
|
||||||
try {
|
try {
|
||||||
assert !sent.isEmpty();
|
assert !sent.isEmpty();
|
||||||
db.addOutstandingBatch(txn, c, batch.getId(), sent);
|
db.addOutstandingBatch(txn, c, batchId, sent);
|
||||||
db.commitTransaction(txn);
|
db.commitTransaction(txn);
|
||||||
return batch;
|
// Don't create another batch if this one was half-empty
|
||||||
|
return bytesSent > Batch.MAX_SIZE / 2;
|
||||||
} catch(DbException e) {
|
} catch(DbException e) {
|
||||||
db.abortTransaction(txn);
|
db.abortTransaction(txn);
|
||||||
throw e;
|
throw e;
|
||||||
@@ -443,9 +433,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
|||||||
|
|
||||||
public void receiveBundle(ContactId c, BundleReader b) throws DbException,
|
public void receiveBundle(ContactId c, BundleReader b) throws DbException,
|
||||||
IOException, GeneralSecurityException {
|
IOException, GeneralSecurityException {
|
||||||
if(LOG.isLoggable(Level.FINE))
|
if(LOG.isLoggable(Level.FINE)) LOG.fine("Received bundle from " + c);
|
||||||
LOG.fine("Received bundle from " + c + ", "
|
|
||||||
+ b.getSize() + " bytes");
|
|
||||||
Header h;
|
Header h;
|
||||||
// Mark all messages in acked batches as seen
|
// Mark all messages in acked batches as seen
|
||||||
contactLock.readLock().lock();
|
contactLock.readLock().lock();
|
||||||
@@ -536,7 +524,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
|||||||
}
|
}
|
||||||
if(LOG.isLoggable(Level.FINE))
|
if(LOG.isLoggable(Level.FINE))
|
||||||
LOG.fine("Received " + batches + " batches");
|
LOG.fine("Received " + batches + " batches");
|
||||||
b.close();
|
b.finish();
|
||||||
retransmitLostBatches(c, h.getId());
|
retransmitLostBatches(c, h.getId());
|
||||||
System.gc();
|
System.gc();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,10 @@ package net.sf.briar.db;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
import java.security.SignatureException;
|
import java.security.SignatureException;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
@@ -16,19 +18,16 @@ import net.sf.briar.api.db.DbException;
|
|||||||
import net.sf.briar.api.db.NoSuchContactException;
|
import net.sf.briar.api.db.NoSuchContactException;
|
||||||
import net.sf.briar.api.protocol.AuthorId;
|
import net.sf.briar.api.protocol.AuthorId;
|
||||||
import net.sf.briar.api.protocol.Batch;
|
import net.sf.briar.api.protocol.Batch;
|
||||||
import net.sf.briar.api.protocol.BatchBuilder;
|
|
||||||
import net.sf.briar.api.protocol.BatchId;
|
import net.sf.briar.api.protocol.BatchId;
|
||||||
import net.sf.briar.api.protocol.BundleId;
|
import net.sf.briar.api.protocol.BundleId;
|
||||||
import net.sf.briar.api.protocol.BundleReader;
|
import net.sf.briar.api.protocol.BundleReader;
|
||||||
import net.sf.briar.api.protocol.BundleWriter;
|
import net.sf.briar.api.protocol.BundleWriter;
|
||||||
import net.sf.briar.api.protocol.GroupId;
|
import net.sf.briar.api.protocol.GroupId;
|
||||||
import net.sf.briar.api.protocol.Header;
|
import net.sf.briar.api.protocol.Header;
|
||||||
import net.sf.briar.api.protocol.HeaderBuilder;
|
|
||||||
import net.sf.briar.api.protocol.Message;
|
import net.sf.briar.api.protocol.Message;
|
||||||
import net.sf.briar.api.protocol.MessageId;
|
import net.sf.briar.api.protocol.MessageId;
|
||||||
|
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Provider;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An implementation of DatabaseComponent using Java synchronization. This
|
* An implementation of DatabaseComponent using Java synchronization. This
|
||||||
@@ -52,10 +51,8 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
|||||||
private final Object transportLock = new Object();
|
private final Object transportLock = new Object();
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
SynchronizedDatabaseComponent(Database<Txn> db, DatabaseCleaner cleaner,
|
SynchronizedDatabaseComponent(Database<Txn> db, DatabaseCleaner cleaner) {
|
||||||
Provider<HeaderBuilder> headerBuilderProvider,
|
super(db, cleaner);
|
||||||
Provider<BatchBuilder> batchBuilderProvider) {
|
|
||||||
super(db, cleaner, headerBuilderProvider, batchBuilderProvider);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void close() throws DbException {
|
public void close() throws DbException {
|
||||||
@@ -148,16 +145,16 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
|||||||
public void generateBundle(ContactId c, BundleWriter b) throws DbException,
|
public void generateBundle(ContactId c, BundleWriter b) throws DbException,
|
||||||
IOException, GeneralSecurityException {
|
IOException, GeneralSecurityException {
|
||||||
if(LOG.isLoggable(Level.FINE)) LOG.fine("Generating bundle for " + c);
|
if(LOG.isLoggable(Level.FINE)) LOG.fine("Generating bundle for " + c);
|
||||||
HeaderBuilder h;
|
Set<BatchId> acks;
|
||||||
|
Set<GroupId> subs;
|
||||||
|
Map<String, String> transports;
|
||||||
// Add acks
|
// Add acks
|
||||||
synchronized(contactLock) {
|
synchronized(contactLock) {
|
||||||
if(!containsContact(c)) throw new NoSuchContactException();
|
if(!containsContact(c)) throw new NoSuchContactException();
|
||||||
h = headerBuilderProvider.get();
|
|
||||||
synchronized(messageStatusLock) {
|
synchronized(messageStatusLock) {
|
||||||
Txn txn = db.startTransaction();
|
Txn txn = db.startTransaction();
|
||||||
try {
|
try {
|
||||||
Set<BatchId> acks = db.removeBatchesToAck(txn, c);
|
acks = db.removeBatchesToAck(txn, c);
|
||||||
h.addAcks(acks);
|
|
||||||
if(LOG.isLoggable(Level.FINE))
|
if(LOG.isLoggable(Level.FINE))
|
||||||
LOG.fine("Added " + acks.size() + " acks");
|
LOG.fine("Added " + acks.size() + " acks");
|
||||||
db.commitTransaction(txn);
|
db.commitTransaction(txn);
|
||||||
@@ -173,8 +170,7 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
|||||||
synchronized(subscriptionLock) {
|
synchronized(subscriptionLock) {
|
||||||
Txn txn = db.startTransaction();
|
Txn txn = db.startTransaction();
|
||||||
try {
|
try {
|
||||||
Set<GroupId> subs = db.getSubscriptions(txn);
|
subs = db.getSubscriptions(txn);
|
||||||
h.addSubscriptions(subs);
|
|
||||||
if(LOG.isLoggable(Level.FINE))
|
if(LOG.isLoggable(Level.FINE))
|
||||||
LOG.fine("Added " + subs.size() + " subscriptions");
|
LOG.fine("Added " + subs.size() + " subscriptions");
|
||||||
db.commitTransaction(txn);
|
db.commitTransaction(txn);
|
||||||
@@ -190,8 +186,7 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
|||||||
synchronized(transportLock) {
|
synchronized(transportLock) {
|
||||||
Txn txn = db.startTransaction();
|
Txn txn = db.startTransaction();
|
||||||
try {
|
try {
|
||||||
Map<String, String> transports = db.getTransports(txn);
|
transports = db.getTransports(txn);
|
||||||
h.addTransports(transports);
|
|
||||||
if(LOG.isLoggable(Level.FINE))
|
if(LOG.isLoggable(Level.FINE))
|
||||||
LOG.fine("Added " + transports.size() + " transports");
|
LOG.fine("Added " + transports.size() + " transports");
|
||||||
db.commitTransaction(txn);
|
db.commitTransaction(txn);
|
||||||
@@ -201,28 +196,16 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Sign the header and add it to the bundle
|
// Add the header to the bundle
|
||||||
Header header = h.build();
|
b.addHeader(acks, subs, transports);
|
||||||
long capacity = b.getCapacity();
|
|
||||||
capacity -= header.getSize();
|
|
||||||
b.addHeader(header);
|
|
||||||
// Add as many messages as possible to the bundle
|
// Add as many messages as possible to the bundle
|
||||||
while(true) {
|
while(fillBatch(c, b));
|
||||||
Batch batch = fillBatch(c, capacity);
|
b.finish();
|
||||||
if(batch == null) break; // No more messages to send
|
|
||||||
b.addBatch(batch);
|
|
||||||
long size = batch.getSize();
|
|
||||||
capacity -= size;
|
|
||||||
// If the batch is less than half full, stop trying - there may be
|
|
||||||
// more messages trickling in but we can't wait forever
|
|
||||||
if(size * 2 < Batch.MAX_SIZE) break;
|
|
||||||
}
|
|
||||||
b.close();
|
|
||||||
if(LOG.isLoggable(Level.FINE)) LOG.fine("Bundle generated");
|
if(LOG.isLoggable(Level.FINE)) LOG.fine("Bundle generated");
|
||||||
System.gc();
|
System.gc();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Batch fillBatch(ContactId c, long capacity) throws DbException,
|
private boolean fillBatch(ContactId c, BundleWriter b) throws DbException,
|
||||||
IOException, GeneralSecurityException {
|
IOException, GeneralSecurityException {
|
||||||
synchronized(contactLock) {
|
synchronized(contactLock) {
|
||||||
if(!containsContact(c)) throw new NoSuchContactException();
|
if(!containsContact(c)) throw new NoSuchContactException();
|
||||||
@@ -230,26 +213,31 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
|||||||
synchronized(messageStatusLock) {
|
synchronized(messageStatusLock) {
|
||||||
Txn txn = db.startTransaction();
|
Txn txn = db.startTransaction();
|
||||||
try {
|
try {
|
||||||
capacity = Math.min(capacity, Batch.MAX_SIZE);
|
long capacity = Math.min(b.getRemainingCapacity(),
|
||||||
|
Batch.MAX_SIZE);
|
||||||
Iterator<MessageId> it =
|
Iterator<MessageId> it =
|
||||||
db.getSendableMessages(txn, c, capacity).iterator();
|
db.getSendableMessages(txn, c, capacity).iterator();
|
||||||
if(!it.hasNext()) {
|
if(!it.hasNext()) {
|
||||||
db.commitTransaction(txn);
|
db.commitTransaction(txn);
|
||||||
return null; // No more messages to send
|
return false; // No more messages to send
|
||||||
}
|
}
|
||||||
BatchBuilder b = batchBuilderProvider.get();
|
|
||||||
Set<MessageId> sent = new HashSet<MessageId>();
|
Set<MessageId> sent = new HashSet<MessageId>();
|
||||||
|
List<Message> messages = new ArrayList<Message>();
|
||||||
|
int bytesSent = 0;
|
||||||
while(it.hasNext()) {
|
while(it.hasNext()) {
|
||||||
MessageId m = it.next();
|
MessageId m = it.next();
|
||||||
b.addMessage(db.getMessage(txn, m));
|
Message message = db.getMessage(txn, m);
|
||||||
|
bytesSent += message.getSize();
|
||||||
|
messages.add(message);
|
||||||
sent.add(m);
|
sent.add(m);
|
||||||
}
|
}
|
||||||
Batch batch = b.build();
|
BatchId batchId = b.addBatch(messages);
|
||||||
// Record the contents of the batch
|
// Record the contents of the batch
|
||||||
assert !sent.isEmpty();
|
assert !sent.isEmpty();
|
||||||
db.addOutstandingBatch(txn, c, batch.getId(), sent);
|
db.addOutstandingBatch(txn, c, batchId, sent);
|
||||||
db.commitTransaction(txn);
|
db.commitTransaction(txn);
|
||||||
return batch;
|
// Don't create another batch if this one was half-empty
|
||||||
|
return bytesSent > Batch.MAX_SIZE / 2;
|
||||||
} catch(DbException e) {
|
} catch(DbException e) {
|
||||||
db.abortTransaction(txn);
|
db.abortTransaction(txn);
|
||||||
throw e;
|
throw e;
|
||||||
@@ -337,9 +325,7 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
|||||||
|
|
||||||
public void receiveBundle(ContactId c, BundleReader b) throws DbException,
|
public void receiveBundle(ContactId c, BundleReader b) throws DbException,
|
||||||
IOException, GeneralSecurityException {
|
IOException, GeneralSecurityException {
|
||||||
if(LOG.isLoggable(Level.FINE))
|
if(LOG.isLoggable(Level.FINE)) LOG.fine("Received bundle from " + c);
|
||||||
LOG.fine("Received bundle from " + c + ", "
|
|
||||||
+ b.getSize() + " bytes");
|
|
||||||
Header h;
|
Header h;
|
||||||
// Mark all messages in acked batches as seen
|
// Mark all messages in acked batches as seen
|
||||||
synchronized(contactLock) {
|
synchronized(contactLock) {
|
||||||
@@ -409,7 +395,7 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
|||||||
}
|
}
|
||||||
if(LOG.isLoggable(Level.FINE))
|
if(LOG.isLoggable(Level.FINE))
|
||||||
LOG.fine("Received " + batches + " batches");
|
LOG.fine("Received " + batches + " batches");
|
||||||
b.close();
|
b.finish();
|
||||||
retransmitLostBatches(c, h.getId());
|
retransmitLostBatches(c, h.getId());
|
||||||
System.gc();
|
System.gc();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
package net.sf.briar.protocol;
|
|
||||||
|
|
||||||
import java.security.KeyPair;
|
|
||||||
import java.security.MessageDigest;
|
|
||||||
import java.security.Signature;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import net.sf.briar.api.protocol.BatchBuilder;
|
|
||||||
import net.sf.briar.api.protocol.Message;
|
|
||||||
import net.sf.briar.api.serial.WriterFactory;
|
|
||||||
|
|
||||||
abstract class BatchBuilderImpl implements BatchBuilder {
|
|
||||||
|
|
||||||
protected final List<Message> messages = new ArrayList<Message>();
|
|
||||||
protected final KeyPair keyPair;
|
|
||||||
protected final Signature signature;
|
|
||||||
protected final MessageDigest messageDigest;
|
|
||||||
protected final WriterFactory writerFactory;
|
|
||||||
|
|
||||||
protected BatchBuilderImpl(KeyPair keyPair, Signature signature,
|
|
||||||
MessageDigest messageDigest, WriterFactory writerFactory) {
|
|
||||||
this.keyPair = keyPair;
|
|
||||||
this.signature = signature;
|
|
||||||
this.messageDigest = messageDigest;
|
|
||||||
this.writerFactory = writerFactory;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addMessage(Message m) {
|
|
||||||
messages.add(m);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
12
components/net/sf/briar/protocol/BatchFactory.java
Normal file
12
components/net/sf/briar/protocol/BatchFactory.java
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
package net.sf.briar.protocol;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import net.sf.briar.api.protocol.Batch;
|
||||||
|
import net.sf.briar.api.protocol.BatchId;
|
||||||
|
import net.sf.briar.api.protocol.Message;
|
||||||
|
|
||||||
|
interface BatchFactory {
|
||||||
|
|
||||||
|
Batch createBatch(BatchId id, List<Message> messages);
|
||||||
|
}
|
||||||
14
components/net/sf/briar/protocol/BatchFactoryImpl.java
Normal file
14
components/net/sf/briar/protocol/BatchFactoryImpl.java
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
package net.sf.briar.protocol;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import net.sf.briar.api.protocol.Batch;
|
||||||
|
import net.sf.briar.api.protocol.BatchId;
|
||||||
|
import net.sf.briar.api.protocol.Message;
|
||||||
|
|
||||||
|
public class BatchFactoryImpl implements BatchFactory {
|
||||||
|
|
||||||
|
public Batch createBatch(BatchId id, List<Message> messages) {
|
||||||
|
return new BatchImpl(id, messages);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,30 +10,18 @@ import net.sf.briar.api.protocol.Message;
|
|||||||
class BatchImpl implements Batch {
|
class BatchImpl implements Batch {
|
||||||
|
|
||||||
private final BatchId id;
|
private final BatchId id;
|
||||||
private final long size;
|
|
||||||
private final List<Message> messages;
|
private final List<Message> messages;
|
||||||
private final byte[] signature;
|
|
||||||
|
|
||||||
BatchImpl(BatchId id, long size, List<Message> messages, byte[] signature) {
|
BatchImpl(BatchId id, List<Message> messages) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.size = size;
|
|
||||||
this.messages = messages;
|
this.messages = messages;
|
||||||
this.signature = signature;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public BatchId getId() {
|
public BatchId getId() {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getSize() {
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Iterable<Message> getMessages() {
|
public Iterable<Message> getMessages() {
|
||||||
return messages;
|
return messages;
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] getSignature() {
|
|
||||||
return signature;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,56 +1,70 @@
|
|||||||
package net.sf.briar.protocol;
|
package net.sf.briar.protocol;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.PublicKey;
|
||||||
|
import java.security.Signature;
|
||||||
|
import java.security.SignatureException;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import net.sf.briar.api.protocol.Batch;
|
import net.sf.briar.api.protocol.Batch;
|
||||||
import net.sf.briar.api.protocol.BatchBuilder;
|
|
||||||
import net.sf.briar.api.protocol.BatchId;
|
import net.sf.briar.api.protocol.BatchId;
|
||||||
|
import net.sf.briar.api.protocol.BundleId;
|
||||||
import net.sf.briar.api.protocol.BundleReader;
|
import net.sf.briar.api.protocol.BundleReader;
|
||||||
import net.sf.briar.api.protocol.GroupId;
|
import net.sf.briar.api.protocol.GroupId;
|
||||||
import net.sf.briar.api.protocol.Header;
|
import net.sf.briar.api.protocol.Header;
|
||||||
import net.sf.briar.api.protocol.HeaderBuilder;
|
|
||||||
import net.sf.briar.api.protocol.Message;
|
import net.sf.briar.api.protocol.Message;
|
||||||
import net.sf.briar.api.protocol.MessageParser;
|
import net.sf.briar.api.protocol.MessageParser;
|
||||||
import net.sf.briar.api.protocol.UniqueId;
|
import net.sf.briar.api.protocol.UniqueId;
|
||||||
import net.sf.briar.api.serial.FormatException;
|
import net.sf.briar.api.serial.FormatException;
|
||||||
import net.sf.briar.api.serial.Raw;
|
import net.sf.briar.api.serial.Raw;
|
||||||
import net.sf.briar.api.serial.Reader;
|
import net.sf.briar.api.serial.Reader;
|
||||||
|
import net.sf.briar.api.serial.ReaderFactory;
|
||||||
import com.google.inject.Provider;
|
|
||||||
|
|
||||||
/** A bundle that deserialises its contents on demand using a reader. */
|
/** A bundle that deserialises its contents on demand using a reader. */
|
||||||
class BundleReaderImpl implements BundleReader {
|
class BundleReaderImpl implements BundleReader {
|
||||||
|
|
||||||
private static enum State { START, FIRST_BATCH, MORE_BATCHES, END };
|
private static enum State { START, FIRST_BATCH, MORE_BATCHES, END };
|
||||||
|
|
||||||
|
private final SigningDigestingInputStream in;
|
||||||
private final Reader r;
|
private final Reader r;
|
||||||
private final long size;
|
private final PublicKey publicKey;
|
||||||
|
private final Signature signature;
|
||||||
|
private final MessageDigest messageDigest;
|
||||||
private final MessageParser messageParser;
|
private final MessageParser messageParser;
|
||||||
private final Provider<HeaderBuilder> headerBuilderProvider;
|
private final HeaderFactory headerFactory;
|
||||||
private final Provider<BatchBuilder> batchBuilderProvider;
|
private final BatchFactory batchFactory;
|
||||||
private State state = State.START;
|
private State state = State.START;
|
||||||
|
|
||||||
BundleReaderImpl(Reader r, long size, MessageParser messageParser,
|
BundleReaderImpl(InputStream in, ReaderFactory readerFactory,
|
||||||
Provider<HeaderBuilder> headerBuilderProvider,
|
PublicKey publicKey, Signature signature,
|
||||||
Provider<BatchBuilder> batchBuilderProvider) {
|
MessageDigest messageDigest, MessageParser messageParser,
|
||||||
this.r = r;
|
HeaderFactory headerFactory, BatchFactory batchFactory) {
|
||||||
this.size = size;
|
this.in = new SigningDigestingInputStream(in, signature, messageDigest);
|
||||||
|
r = readerFactory.createReader(this.in);
|
||||||
|
this.publicKey = publicKey;
|
||||||
|
this.signature = signature;
|
||||||
|
this.messageDigest = messageDigest;
|
||||||
this.messageParser = messageParser;
|
this.messageParser = messageParser;
|
||||||
this.headerBuilderProvider = headerBuilderProvider;
|
this.headerFactory = headerFactory;
|
||||||
this.batchBuilderProvider = batchBuilderProvider;
|
this.batchFactory = batchFactory;
|
||||||
}
|
|
||||||
|
|
||||||
public long getSize() {
|
|
||||||
return size;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Header getHeader() throws IOException, GeneralSecurityException {
|
public Header getHeader() throws IOException, GeneralSecurityException {
|
||||||
if(state != State.START) throw new IllegalStateException();
|
if(state != State.START) throw new IllegalStateException();
|
||||||
|
state = State.FIRST_BATCH;
|
||||||
|
// Initialise the input stream
|
||||||
|
signature.initVerify(publicKey);
|
||||||
|
messageDigest.reset();
|
||||||
|
// Read the signed data
|
||||||
|
in.setDigesting(true);
|
||||||
|
in.setSigning(true);
|
||||||
r.setReadLimit(Header.MAX_SIZE);
|
r.setReadLimit(Header.MAX_SIZE);
|
||||||
Set<BatchId> acks = new HashSet<BatchId>();
|
Set<BatchId> acks = new HashSet<BatchId>();
|
||||||
for(Raw raw : r.readList(Raw.class)) {
|
for(Raw raw : r.readList(Raw.class)) {
|
||||||
@@ -64,15 +78,16 @@ class BundleReaderImpl implements BundleReader {
|
|||||||
if(b.length != UniqueId.LENGTH) throw new FormatException();
|
if(b.length != UniqueId.LENGTH) throw new FormatException();
|
||||||
subs.add(new GroupId(b));
|
subs.add(new GroupId(b));
|
||||||
}
|
}
|
||||||
Map<String, String> transports = r.readMap(String.class, String.class);
|
Map<String, String> transports =
|
||||||
|
r.readMap(String.class, String.class);
|
||||||
|
in.setSigning(false);
|
||||||
|
// Read and verify the signature
|
||||||
byte[] sig = r.readRaw();
|
byte[] sig = r.readRaw();
|
||||||
state = State.FIRST_BATCH;
|
in.setDigesting(false);
|
||||||
HeaderBuilder h = headerBuilderProvider.get();
|
if(!signature.verify(sig)) throw new SignatureException();
|
||||||
h.addAcks(acks);
|
// Build and return the header
|
||||||
h.addSubscriptions(subs);
|
BundleId id = new BundleId(messageDigest.digest());
|
||||||
h.addTransports(transports);
|
return headerFactory.createHeader(id, acks, subs, transports);
|
||||||
h.setSignature(sig);
|
|
||||||
return h.build();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Batch getNextBatch() throws IOException, GeneralSecurityException {
|
public Batch getNextBatch() throws IOException, GeneralSecurityException {
|
||||||
@@ -86,19 +101,31 @@ class BundleReaderImpl implements BundleReader {
|
|||||||
state = State.END;
|
state = State.END;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
// Initialise the input stream
|
||||||
|
signature.initVerify(publicKey);
|
||||||
|
messageDigest.reset();
|
||||||
|
// Read the signed data
|
||||||
|
in.setDigesting(true);
|
||||||
|
in.setSigning(true);
|
||||||
r.setReadLimit(Batch.MAX_SIZE);
|
r.setReadLimit(Batch.MAX_SIZE);
|
||||||
List<Raw> messages = r.readList(Raw.class);
|
List<Raw> rawMessages = r.readList(Raw.class);
|
||||||
BatchBuilder b = batchBuilderProvider.get();
|
in.setSigning(false);
|
||||||
for(Raw r : messages) {
|
// Read and verify the signature
|
||||||
Message m = messageParser.parseMessage(r.getBytes());
|
|
||||||
b.addMessage(m);
|
|
||||||
}
|
|
||||||
byte[] sig = r.readRaw();
|
byte[] sig = r.readRaw();
|
||||||
b.setSignature(sig);
|
in.setDigesting(false);
|
||||||
return b.build();
|
if(!signature.verify(sig)) throw new SignatureException();
|
||||||
|
// Parse the messages
|
||||||
|
List<Message> messages = new ArrayList<Message>(rawMessages.size());
|
||||||
|
for(Raw r : rawMessages) {
|
||||||
|
Message m = messageParser.parseMessage(r.getBytes());
|
||||||
|
messages.add(m);
|
||||||
|
}
|
||||||
|
// Build and return the batch
|
||||||
|
BatchId id = new BatchId(messageDigest.digest());
|
||||||
|
return batchFactory.createBatch(id, messages);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void close() throws IOException {
|
public void finish() throws IOException {
|
||||||
r.close();
|
r.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,59 +1,100 @@
|
|||||||
package net.sf.briar.protocol;
|
package net.sf.briar.protocol;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.security.DigestOutputStream;
|
||||||
|
import java.security.GeneralSecurityException;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.PrivateKey;
|
||||||
|
import java.security.Signature;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import net.sf.briar.api.protocol.Batch;
|
|
||||||
import net.sf.briar.api.protocol.BatchId;
|
import net.sf.briar.api.protocol.BatchId;
|
||||||
|
import net.sf.briar.api.protocol.BundleId;
|
||||||
import net.sf.briar.api.protocol.BundleWriter;
|
import net.sf.briar.api.protocol.BundleWriter;
|
||||||
import net.sf.briar.api.protocol.GroupId;
|
import net.sf.briar.api.protocol.GroupId;
|
||||||
import net.sf.briar.api.protocol.Header;
|
|
||||||
import net.sf.briar.api.protocol.Message;
|
import net.sf.briar.api.protocol.Message;
|
||||||
import net.sf.briar.api.serial.Writer;
|
import net.sf.briar.api.serial.Writer;
|
||||||
|
import net.sf.briar.api.serial.WriterFactory;
|
||||||
|
|
||||||
/** A bundle builder that serialises its contents using a writer. */
|
/** A bundle builder that serialises its contents using a writer. */
|
||||||
class BundleWriterImpl implements BundleWriter {
|
class BundleWriterImpl implements BundleWriter {
|
||||||
|
|
||||||
private static enum State { START, FIRST_BATCH, MORE_BATCHES, END };
|
private static enum State { START, FIRST_BATCH, MORE_BATCHES, END };
|
||||||
|
|
||||||
|
private final SigningOutputStream out;
|
||||||
private final Writer w;
|
private final Writer w;
|
||||||
|
private final PrivateKey privateKey;
|
||||||
|
private final Signature signature;
|
||||||
|
private final MessageDigest messageDigest;
|
||||||
private final long capacity;
|
private final long capacity;
|
||||||
private State state = State.START;
|
private State state = State.START;
|
||||||
|
|
||||||
BundleWriterImpl(Writer w, long capacity) {
|
BundleWriterImpl(OutputStream out, WriterFactory writerFactory,
|
||||||
this.w = w;
|
PrivateKey privateKey, Signature signature,
|
||||||
|
MessageDigest messageDigest, long capacity) {
|
||||||
|
OutputStream out1 = new DigestOutputStream(out, messageDigest);
|
||||||
|
this.out = new SigningOutputStream(out1, signature);
|
||||||
|
w = writerFactory.createWriter(this.out);
|
||||||
|
this.privateKey = privateKey;
|
||||||
|
this.signature = signature;
|
||||||
|
this.messageDigest = messageDigest;
|
||||||
this.capacity = capacity;
|
this.capacity = capacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getCapacity() {
|
public long getRemainingCapacity() {
|
||||||
return capacity;
|
return capacity - w.getRawBytesWritten();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addHeader(Header h) throws IOException {
|
public BundleId addHeader(Iterable<BatchId> acks, Iterable<GroupId> subs,
|
||||||
|
Map<String, String> transports) throws IOException,
|
||||||
|
GeneralSecurityException {
|
||||||
if(state != State.START) throw new IllegalStateException();
|
if(state != State.START) throw new IllegalStateException();
|
||||||
|
// Initialise the output stream
|
||||||
|
signature.initSign(privateKey);
|
||||||
|
messageDigest.reset();
|
||||||
|
// Write the data to be signed
|
||||||
|
out.setSigning(true);
|
||||||
w.writeListStart();
|
w.writeListStart();
|
||||||
for(BatchId ack : h.getAcks()) w.writeRaw(ack);
|
for(BatchId ack : acks) w.writeRaw(ack);
|
||||||
w.writeListEnd();
|
w.writeListEnd();
|
||||||
w.writeListStart();
|
w.writeListStart();
|
||||||
for(GroupId sub : h.getSubscriptions()) w.writeRaw(sub);
|
for(GroupId sub : subs) w.writeRaw(sub);
|
||||||
w.writeListEnd();
|
w.writeListEnd();
|
||||||
w.writeMap(h.getTransports());
|
w.writeMap(transports);
|
||||||
w.writeRaw(h.getSignature());
|
out.setSigning(false);
|
||||||
|
// Create and write the signature
|
||||||
|
byte[] sig = signature.sign();
|
||||||
|
w.writeRaw(sig);
|
||||||
|
// Calculate and return the ID
|
||||||
state = State.FIRST_BATCH;
|
state = State.FIRST_BATCH;
|
||||||
|
return new BundleId(messageDigest.digest());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addBatch(Batch b) throws IOException {
|
public BatchId addBatch(Iterable<Message> messages) throws IOException,
|
||||||
|
GeneralSecurityException {
|
||||||
if(state == State.FIRST_BATCH) {
|
if(state == State.FIRST_BATCH) {
|
||||||
w.writeListStart();
|
w.writeListStart();
|
||||||
state = State.MORE_BATCHES;
|
state = State.MORE_BATCHES;
|
||||||
}
|
}
|
||||||
if(state != State.MORE_BATCHES) throw new IllegalStateException();
|
if(state != State.MORE_BATCHES) throw new IllegalStateException();
|
||||||
|
// Initialise the output stream
|
||||||
|
signature.initSign(privateKey);
|
||||||
|
messageDigest.reset();
|
||||||
|
// Write the data to be signed
|
||||||
|
out.setSigning(true);
|
||||||
w.writeListStart();
|
w.writeListStart();
|
||||||
for(Message m : b.getMessages()) w.writeRaw(m.getBytes());
|
for(Message m : messages) w.writeRaw(m);
|
||||||
w.writeListEnd();
|
w.writeListEnd();
|
||||||
w.writeRaw(b.getSignature());
|
out.setSigning(false);
|
||||||
|
// Create and write the signature
|
||||||
|
byte[] sig = signature.sign();
|
||||||
|
w.writeRaw(sig);
|
||||||
|
// Calculate and return the ID
|
||||||
|
return new BatchId(messageDigest.digest());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void close() throws IOException {
|
public void finish() throws IOException {
|
||||||
if(state == State.FIRST_BATCH) {
|
if(state == State.FIRST_BATCH) {
|
||||||
w.writeListStart();
|
w.writeListStart();
|
||||||
state = State.MORE_BATCHES;
|
state = State.MORE_BATCHES;
|
||||||
|
|||||||
@@ -1,49 +0,0 @@
|
|||||||
package net.sf.briar.protocol;
|
|
||||||
|
|
||||||
import java.security.KeyPair;
|
|
||||||
import java.security.MessageDigest;
|
|
||||||
import java.security.Signature;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import net.sf.briar.api.protocol.BatchId;
|
|
||||||
import net.sf.briar.api.protocol.GroupId;
|
|
||||||
import net.sf.briar.api.protocol.HeaderBuilder;
|
|
||||||
import net.sf.briar.api.serial.WriterFactory;
|
|
||||||
|
|
||||||
abstract class HeaderBuilderImpl implements HeaderBuilder {
|
|
||||||
|
|
||||||
protected final List<BatchId> acks = new ArrayList<BatchId>();
|
|
||||||
protected final List<GroupId> subs = new ArrayList<GroupId>();
|
|
||||||
protected final Map<String, String> transports =
|
|
||||||
new LinkedHashMap<String, String>();
|
|
||||||
|
|
||||||
protected final KeyPair keyPair;
|
|
||||||
protected final Signature signature;
|
|
||||||
protected final MessageDigest messageDigest;
|
|
||||||
protected final WriterFactory writerFactory;
|
|
||||||
|
|
||||||
protected HeaderBuilderImpl(KeyPair keyPair, Signature signature,
|
|
||||||
MessageDigest messageDigest, WriterFactory writerFactory) {
|
|
||||||
this.keyPair = keyPair;
|
|
||||||
this.signature = signature;
|
|
||||||
this.messageDigest = messageDigest;
|
|
||||||
this.writerFactory = writerFactory;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addAcks(Iterable<BatchId> acks) {
|
|
||||||
for(BatchId ack : acks) this.acks.add(ack);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addSubscriptions(Iterable<GroupId> subs) {
|
|
||||||
for(GroupId sub : subs) this.subs.add(sub);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addTransports(Map<String, String> transports) {
|
|
||||||
for(String key : transports.keySet()) {
|
|
||||||
this.transports.put(key, transports.get(key));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
15
components/net/sf/briar/protocol/HeaderFactory.java
Normal file
15
components/net/sf/briar/protocol/HeaderFactory.java
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
package net.sf.briar.protocol;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import net.sf.briar.api.protocol.BatchId;
|
||||||
|
import net.sf.briar.api.protocol.BundleId;
|
||||||
|
import net.sf.briar.api.protocol.GroupId;
|
||||||
|
import net.sf.briar.api.protocol.Header;
|
||||||
|
|
||||||
|
interface HeaderFactory {
|
||||||
|
|
||||||
|
Header createHeader(BundleId id, Set<BatchId> acks, Set<GroupId> subs,
|
||||||
|
Map<String, String> transports);
|
||||||
|
}
|
||||||
17
components/net/sf/briar/protocol/HeaderFactoryImpl.java
Normal file
17
components/net/sf/briar/protocol/HeaderFactoryImpl.java
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package net.sf.briar.protocol;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import net.sf.briar.api.protocol.BatchId;
|
||||||
|
import net.sf.briar.api.protocol.BundleId;
|
||||||
|
import net.sf.briar.api.protocol.GroupId;
|
||||||
|
import net.sf.briar.api.protocol.Header;
|
||||||
|
|
||||||
|
class HeaderFactoryImpl implements HeaderFactory {
|
||||||
|
|
||||||
|
public Header createHeader(BundleId id, Set<BatchId> acks,
|
||||||
|
Set<GroupId> subs, Map<String, String> transports) {
|
||||||
|
return new HeaderImpl(id, acks, subs, transports);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,44 +12,31 @@ import net.sf.briar.api.protocol.Header;
|
|||||||
class HeaderImpl implements Header {
|
class HeaderImpl implements Header {
|
||||||
|
|
||||||
private final BundleId id;
|
private final BundleId id;
|
||||||
private final long size;
|
|
||||||
private final Set<BatchId> acks;
|
private final Set<BatchId> acks;
|
||||||
private final Set<GroupId> subscriptions;
|
private final Set<GroupId> subs;
|
||||||
private final Map<String, String> transports;
|
private final Map<String, String> transports;
|
||||||
private final byte[] signature;
|
|
||||||
|
|
||||||
HeaderImpl(BundleId id, long size, Set<BatchId> acks,
|
HeaderImpl(BundleId id, Set<BatchId> acks, Set<GroupId> subs,
|
||||||
Set<GroupId> subscriptions, Map<String, String> transports,
|
Map<String, String> transports) {
|
||||||
byte[] signature) {
|
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.size = size;
|
|
||||||
this.acks = acks;
|
this.acks = acks;
|
||||||
this.subscriptions = subscriptions;
|
this.subs = subs;
|
||||||
this.transports = transports;
|
this.transports = transports;
|
||||||
this.signature = signature;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public BundleId getId() {
|
public BundleId getId() {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getSize() {
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Set<BatchId> getAcks() {
|
public Set<BatchId> getAcks() {
|
||||||
return acks;
|
return acks;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<GroupId> getSubscriptions() {
|
public Set<GroupId> getSubscriptions() {
|
||||||
return subscriptions;
|
return subs;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, String> getTransports() {
|
public Map<String, String> getTransports() {
|
||||||
return transports;
|
return transports;
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] getSignature() {
|
|
||||||
return signature;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,46 +0,0 @@
|
|||||||
package net.sf.briar.protocol;
|
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.security.GeneralSecurityException;
|
|
||||||
import java.security.KeyPair;
|
|
||||||
import java.security.MessageDigest;
|
|
||||||
import java.security.Signature;
|
|
||||||
import java.security.SignatureException;
|
|
||||||
|
|
||||||
import net.sf.briar.api.protocol.Batch;
|
|
||||||
import net.sf.briar.api.protocol.BatchId;
|
|
||||||
import net.sf.briar.api.serial.Writer;
|
|
||||||
import net.sf.briar.api.serial.WriterFactory;
|
|
||||||
|
|
||||||
public class IncomingBatchBuilder extends BatchBuilderImpl {
|
|
||||||
|
|
||||||
IncomingBatchBuilder(KeyPair keyPair, Signature signature,
|
|
||||||
MessageDigest messageDigest, WriterFactory writerFactory) {
|
|
||||||
super(keyPair, signature, messageDigest, writerFactory);
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] sig = null;
|
|
||||||
|
|
||||||
public void setSignature(byte[] sig) {
|
|
||||||
this.sig = sig;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Batch build() throws IOException, GeneralSecurityException {
|
|
||||||
if(sig == null) throw new IllegalStateException();
|
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
||||||
Writer w = writerFactory.createWriter(out);
|
|
||||||
w.writeList(messages);
|
|
||||||
byte[] signable = out.toByteArray();
|
|
||||||
signature.initVerify(keyPair.getPublic());
|
|
||||||
signature.update(signable);
|
|
||||||
if(!signature.verify(sig)) throw new SignatureException();
|
|
||||||
w.writeRaw(sig);
|
|
||||||
w.close();
|
|
||||||
byte[] raw = out.toByteArray();
|
|
||||||
messageDigest.reset();
|
|
||||||
messageDigest.update(raw);
|
|
||||||
BatchId id = new BatchId(messageDigest.digest());
|
|
||||||
return new BatchImpl(id, raw.length, messages, sig);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
package net.sf.briar.protocol;
|
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.security.GeneralSecurityException;
|
|
||||||
import java.security.KeyPair;
|
|
||||||
import java.security.MessageDigest;
|
|
||||||
import java.security.Signature;
|
|
||||||
import java.security.SignatureException;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import net.sf.briar.api.protocol.BatchId;
|
|
||||||
import net.sf.briar.api.protocol.BundleId;
|
|
||||||
import net.sf.briar.api.protocol.GroupId;
|
|
||||||
import net.sf.briar.api.protocol.Header;
|
|
||||||
import net.sf.briar.api.serial.Writer;
|
|
||||||
import net.sf.briar.api.serial.WriterFactory;
|
|
||||||
|
|
||||||
class IncomingHeaderBuilder extends HeaderBuilderImpl {
|
|
||||||
|
|
||||||
private byte[] sig = null;
|
|
||||||
|
|
||||||
IncomingHeaderBuilder(KeyPair keyPair, Signature signature,
|
|
||||||
MessageDigest messageDigest, WriterFactory writerFactory) {
|
|
||||||
super(keyPair, signature, messageDigest, writerFactory);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSignature(byte[] sig) {
|
|
||||||
this.sig = sig;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Header build() throws IOException, GeneralSecurityException {
|
|
||||||
if(sig == null) throw new IllegalStateException();
|
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
||||||
Writer w = writerFactory.createWriter(out);
|
|
||||||
w.writeList(acks);
|
|
||||||
w.writeList(subs);
|
|
||||||
w.writeMap(transports);
|
|
||||||
byte[] signable = out.toByteArray();
|
|
||||||
signature.initVerify(keyPair.getPublic());
|
|
||||||
signature.update(signable);
|
|
||||||
if(!signature.verify(sig)) throw new SignatureException();
|
|
||||||
w.writeRaw(sig);
|
|
||||||
w.close();
|
|
||||||
byte[] raw = out.toByteArray();
|
|
||||||
messageDigest.reset();
|
|
||||||
messageDigest.update(raw);
|
|
||||||
BundleId id = new BundleId(messageDigest.digest());
|
|
||||||
Set<BatchId> ackSet = new HashSet<BatchId>(acks);
|
|
||||||
Set<GroupId> subSet = new HashSet<GroupId>(subs);
|
|
||||||
return new HeaderImpl(id, raw.length, ackSet, subSet, transports, sig);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
package net.sf.briar.protocol;
|
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.security.GeneralSecurityException;
|
|
||||||
import java.security.KeyPair;
|
|
||||||
import java.security.MessageDigest;
|
|
||||||
import java.security.Signature;
|
|
||||||
|
|
||||||
import net.sf.briar.api.protocol.Batch;
|
|
||||||
import net.sf.briar.api.protocol.BatchId;
|
|
||||||
import net.sf.briar.api.serial.Writer;
|
|
||||||
import net.sf.briar.api.serial.WriterFactory;
|
|
||||||
|
|
||||||
public class OutgoingBatchBuilder extends BatchBuilderImpl {
|
|
||||||
|
|
||||||
OutgoingBatchBuilder(KeyPair keyPair, Signature signature,
|
|
||||||
MessageDigest messageDigest, WriterFactory writerFactory) {
|
|
||||||
super(keyPair, signature, messageDigest, writerFactory);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSignature(byte[] sig) {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Batch build() throws IOException, GeneralSecurityException {
|
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
||||||
Writer w = writerFactory.createWriter(out);
|
|
||||||
w.writeList(messages);
|
|
||||||
byte[] signable = out.toByteArray();
|
|
||||||
signature.initSign(keyPair.getPrivate());
|
|
||||||
signature.update(signable);
|
|
||||||
byte[] sig = signature.sign();
|
|
||||||
w.writeRaw(sig);
|
|
||||||
w.close();
|
|
||||||
byte[] raw = out.toByteArray();
|
|
||||||
messageDigest.reset();
|
|
||||||
messageDigest.update(raw);
|
|
||||||
BatchId id = new BatchId(messageDigest.digest());
|
|
||||||
return new BatchImpl(id, raw.length, messages, sig);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
package net.sf.briar.protocol;
|
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.security.GeneralSecurityException;
|
|
||||||
import java.security.KeyPair;
|
|
||||||
import java.security.MessageDigest;
|
|
||||||
import java.security.Signature;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import net.sf.briar.api.protocol.BatchId;
|
|
||||||
import net.sf.briar.api.protocol.BundleId;
|
|
||||||
import net.sf.briar.api.protocol.GroupId;
|
|
||||||
import net.sf.briar.api.protocol.Header;
|
|
||||||
import net.sf.briar.api.serial.Writer;
|
|
||||||
import net.sf.briar.api.serial.WriterFactory;
|
|
||||||
|
|
||||||
public class OutgoingHeaderBuilder extends HeaderBuilderImpl {
|
|
||||||
|
|
||||||
OutgoingHeaderBuilder(KeyPair keyPair, Signature signature,
|
|
||||||
MessageDigest messageDigest, WriterFactory writerFactory) {
|
|
||||||
super(keyPair, signature, messageDigest, writerFactory);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSignature(byte[] sig) {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Header build() throws IOException, GeneralSecurityException {
|
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
||||||
Writer w = writerFactory.createWriter(out);
|
|
||||||
w.writeList(acks);
|
|
||||||
w.writeList(subs);
|
|
||||||
w.writeMap(transports);
|
|
||||||
byte[] signable = out.toByteArray();
|
|
||||||
signature.initSign(keyPair.getPrivate());
|
|
||||||
signature.update(signable);
|
|
||||||
byte[] sig = signature.sign();
|
|
||||||
w.writeRaw(sig);
|
|
||||||
w.close();
|
|
||||||
byte[] raw = out.toByteArray();
|
|
||||||
messageDigest.reset();
|
|
||||||
messageDigest.update(raw);
|
|
||||||
BundleId id = new BundleId(messageDigest.digest());
|
|
||||||
Set<BatchId> ackSet = new HashSet<BatchId>(acks);
|
|
||||||
Set<GroupId> subSet = new HashSet<GroupId>(subs);
|
|
||||||
return new HeaderImpl(id, raw.length, ackSet, subSet, transports, sig);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,116 @@
|
|||||||
|
package net.sf.briar.protocol;
|
||||||
|
|
||||||
|
import java.io.FilterInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.Signature;
|
||||||
|
import java.security.SignatureException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An input stream that passes its input through a signature and a message
|
||||||
|
* digest. The signature and message digest lag behind the input by one byte
|
||||||
|
* until the end of the input is reached, to allow users of this class to
|
||||||
|
* maintain one byte of lookahead without affecting the signature or digest.
|
||||||
|
*/
|
||||||
|
class SigningDigestingInputStream extends FilterInputStream {
|
||||||
|
|
||||||
|
private final Signature signature;
|
||||||
|
private final MessageDigest messageDigest;
|
||||||
|
private byte nextByte = 0;
|
||||||
|
private boolean started = false, eof = false;
|
||||||
|
private boolean signing = false, digesting = false;
|
||||||
|
|
||||||
|
protected SigningDigestingInputStream(InputStream in, Signature signature,
|
||||||
|
MessageDigest messageDigest) {
|
||||||
|
super(in);
|
||||||
|
this.signature = signature;
|
||||||
|
this.messageDigest = messageDigest;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSigning(boolean signing) {
|
||||||
|
this.signing = signing;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDigesting(boolean digesting) {
|
||||||
|
this.digesting = digesting;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void write(byte b) throws IOException {
|
||||||
|
if(signing) {
|
||||||
|
try {
|
||||||
|
signature.update(b);
|
||||||
|
} catch(SignatureException e) {
|
||||||
|
throw new IOException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(digesting) messageDigest.update(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void write(byte[] b, int off, int len) throws IOException {
|
||||||
|
if(signing) {
|
||||||
|
try {
|
||||||
|
signature.update(b, off, len);
|
||||||
|
} catch(SignatureException e) {
|
||||||
|
throw new IOException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(digesting) messageDigest.update(b, off, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mark(int readLimit) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean markSupported() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read() throws IOException {
|
||||||
|
if(eof) return -1;
|
||||||
|
if(started) write(nextByte);
|
||||||
|
started = true;
|
||||||
|
int i = in.read();
|
||||||
|
if(i == -1) {
|
||||||
|
eof = true;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
nextByte = (byte) (i > 127 ? i - 256 : i);
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read(byte[] b) throws IOException {
|
||||||
|
return read(b, 0, b.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read(byte[] b, int off, int len) throws IOException {
|
||||||
|
if(eof) return -1;
|
||||||
|
if(started) write(nextByte);
|
||||||
|
started = true;
|
||||||
|
int read = in.read(b, off, len);
|
||||||
|
if(read == -1) {
|
||||||
|
eof = true;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if(read > 0) {
|
||||||
|
write(b, off, read - 1);
|
||||||
|
nextByte = b[off + read - 1];
|
||||||
|
}
|
||||||
|
return read;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void reset() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long skip(long n) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
52
components/net/sf/briar/protocol/SigningOutputStream.java
Normal file
52
components/net/sf/briar/protocol/SigningOutputStream.java
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
package net.sf.briar.protocol;
|
||||||
|
|
||||||
|
import java.io.FilterOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.security.Signature;
|
||||||
|
import java.security.SignatureException;
|
||||||
|
|
||||||
|
/** An output stream that passes its output through a signature. */
|
||||||
|
class SigningOutputStream extends FilterOutputStream {
|
||||||
|
|
||||||
|
private final Signature signature;
|
||||||
|
private boolean signing = false;
|
||||||
|
|
||||||
|
public SigningOutputStream(OutputStream out, Signature signature) {
|
||||||
|
super(out);
|
||||||
|
this.signature = signature;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSigning(boolean signing) {
|
||||||
|
this.signing = signing;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(byte[] b) throws IOException {
|
||||||
|
write(b, 0, b.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(byte[] b, int off, int len) throws IOException {
|
||||||
|
out.write(b, off, len);
|
||||||
|
if(signing) {
|
||||||
|
try {
|
||||||
|
signature.update(b, off, len);
|
||||||
|
} catch(SignatureException e) {
|
||||||
|
throw new IOException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(int b) throws IOException {
|
||||||
|
out.write(b);
|
||||||
|
if(signing) {
|
||||||
|
try {
|
||||||
|
signature.update((byte) b);
|
||||||
|
} catch(SignatureException e) {
|
||||||
|
throw new IOException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,8 +6,6 @@ import static net.sf.briar.api.db.DatabaseComponent.MIN_FREE_SPACE;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
||||||
import net.sf.briar.api.db.DbException;
|
import net.sf.briar.api.db.DbException;
|
||||||
import net.sf.briar.api.protocol.BatchBuilder;
|
|
||||||
import net.sf.briar.api.protocol.HeaderBuilder;
|
|
||||||
import net.sf.briar.api.protocol.MessageId;
|
import net.sf.briar.api.protocol.MessageId;
|
||||||
import net.sf.briar.db.DatabaseCleaner.Callback;
|
import net.sf.briar.db.DatabaseCleaner.Callback;
|
||||||
|
|
||||||
@@ -15,8 +13,6 @@ import org.jmock.Expectations;
|
|||||||
import org.jmock.Mockery;
|
import org.jmock.Mockery;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import com.google.inject.Provider;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests that use the DatabaseCleaner.Callback interface of
|
* Tests that use the DatabaseCleaner.Callback interface of
|
||||||
* DatabaseComponentImpl.
|
* DatabaseComponentImpl.
|
||||||
@@ -24,9 +20,7 @@ import com.google.inject.Provider;
|
|||||||
public abstract class DatabaseComponentImplTest extends DatabaseComponentTest {
|
public abstract class DatabaseComponentImplTest extends DatabaseComponentTest {
|
||||||
|
|
||||||
protected abstract <T> DatabaseComponentImpl<T> createDatabaseComponentImpl(
|
protected abstract <T> DatabaseComponentImpl<T> createDatabaseComponentImpl(
|
||||||
Database<T> database, DatabaseCleaner cleaner,
|
Database<T> database, DatabaseCleaner cleaner);
|
||||||
Provider<HeaderBuilder> headerBuilderProvider,
|
|
||||||
Provider<BatchBuilder> batchBuilderProvider);
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNotCleanedIfEnoughFreeSpace() throws DbException {
|
public void testNotCleanedIfEnoughFreeSpace() throws DbException {
|
||||||
@@ -34,18 +28,11 @@ public abstract class DatabaseComponentImplTest extends DatabaseComponentTest {
|
|||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
final Database<Object> database = context.mock(Database.class);
|
final Database<Object> database = context.mock(Database.class);
|
||||||
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
final Provider<HeaderBuilder> headerBuilderProvider =
|
|
||||||
context.mock(Provider.class);
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
final Provider<BatchBuilder> batchBuilderProvider =
|
|
||||||
context.mock(Provider.class, "batchBuilderProvider");
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(database).getFreeSpace();
|
oneOf(database).getFreeSpace();
|
||||||
will(returnValue(MIN_FREE_SPACE));
|
will(returnValue(MIN_FREE_SPACE));
|
||||||
}});
|
}});
|
||||||
Callback db = createDatabaseComponentImpl(database, cleaner,
|
Callback db = createDatabaseComponentImpl(database, cleaner);
|
||||||
headerBuilderProvider, batchBuilderProvider);
|
|
||||||
|
|
||||||
db.checkFreeSpaceAndClean();
|
db.checkFreeSpaceAndClean();
|
||||||
|
|
||||||
@@ -58,12 +45,6 @@ public abstract class DatabaseComponentImplTest extends DatabaseComponentTest {
|
|||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
final Database<Object> database = context.mock(Database.class);
|
final Database<Object> database = context.mock(Database.class);
|
||||||
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
final Provider<HeaderBuilder> headerBuilderProvider =
|
|
||||||
context.mock(Provider.class);
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
final Provider<BatchBuilder> batchBuilderProvider =
|
|
||||||
context.mock(Provider.class, "batchBuilderProvider");
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(database).getFreeSpace();
|
oneOf(database).getFreeSpace();
|
||||||
will(returnValue(MIN_FREE_SPACE - 1));
|
will(returnValue(MIN_FREE_SPACE - 1));
|
||||||
@@ -76,8 +57,7 @@ public abstract class DatabaseComponentImplTest extends DatabaseComponentTest {
|
|||||||
oneOf(database).getFreeSpace();
|
oneOf(database).getFreeSpace();
|
||||||
will(returnValue(MIN_FREE_SPACE));
|
will(returnValue(MIN_FREE_SPACE));
|
||||||
}});
|
}});
|
||||||
Callback db = createDatabaseComponentImpl(database, cleaner,
|
Callback db = createDatabaseComponentImpl(database, cleaner);
|
||||||
headerBuilderProvider, batchBuilderProvider);
|
|
||||||
|
|
||||||
db.checkFreeSpaceAndClean();
|
db.checkFreeSpaceAndClean();
|
||||||
|
|
||||||
@@ -91,12 +71,6 @@ public abstract class DatabaseComponentImplTest extends DatabaseComponentTest {
|
|||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
final Database<Object> database = context.mock(Database.class);
|
final Database<Object> database = context.mock(Database.class);
|
||||||
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
final Provider<HeaderBuilder> headerBuilderProvider =
|
|
||||||
context.mock(Provider.class);
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
final Provider<BatchBuilder> batchBuilderProvider =
|
|
||||||
context.mock(Provider.class, "batchBuilderProvider");
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(database).getFreeSpace();
|
oneOf(database).getFreeSpace();
|
||||||
will(returnValue(MIN_FREE_SPACE - 1));
|
will(returnValue(MIN_FREE_SPACE - 1));
|
||||||
@@ -111,8 +85,7 @@ public abstract class DatabaseComponentImplTest extends DatabaseComponentTest {
|
|||||||
oneOf(database).getFreeSpace();
|
oneOf(database).getFreeSpace();
|
||||||
will(returnValue(MIN_FREE_SPACE));
|
will(returnValue(MIN_FREE_SPACE));
|
||||||
}});
|
}});
|
||||||
Callback db = createDatabaseComponentImpl(database, cleaner,
|
Callback db = createDatabaseComponentImpl(database, cleaner);
|
||||||
headerBuilderProvider, batchBuilderProvider);
|
|
||||||
|
|
||||||
db.checkFreeSpaceAndClean();
|
db.checkFreeSpaceAndClean();
|
||||||
|
|
||||||
@@ -126,12 +99,6 @@ public abstract class DatabaseComponentImplTest extends DatabaseComponentTest {
|
|||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
final Database<Object> database = context.mock(Database.class);
|
final Database<Object> database = context.mock(Database.class);
|
||||||
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
final Provider<HeaderBuilder> headerBuilderProvider =
|
|
||||||
context.mock(Provider.class);
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
final Provider<BatchBuilder> batchBuilderProvider =
|
|
||||||
context.mock(Provider.class, "batchBuilderProvider");
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(database).getFreeSpace();
|
oneOf(database).getFreeSpace();
|
||||||
will(returnValue(MIN_FREE_SPACE - 1));
|
will(returnValue(MIN_FREE_SPACE - 1));
|
||||||
@@ -148,8 +115,7 @@ public abstract class DatabaseComponentImplTest extends DatabaseComponentTest {
|
|||||||
oneOf(database).getFreeSpace();
|
oneOf(database).getFreeSpace();
|
||||||
will(returnValue(MIN_FREE_SPACE));
|
will(returnValue(MIN_FREE_SPACE));
|
||||||
}});
|
}});
|
||||||
Callback db = createDatabaseComponentImpl(database, cleaner,
|
Callback db = createDatabaseComponentImpl(database, cleaner);
|
||||||
headerBuilderProvider, batchBuilderProvider);
|
|
||||||
|
|
||||||
db.checkFreeSpaceAndClean();
|
db.checkFreeSpaceAndClean();
|
||||||
|
|
||||||
|
|||||||
@@ -14,14 +14,12 @@ import net.sf.briar.api.db.NoSuchContactException;
|
|||||||
import net.sf.briar.api.db.Status;
|
import net.sf.briar.api.db.Status;
|
||||||
import net.sf.briar.api.protocol.AuthorId;
|
import net.sf.briar.api.protocol.AuthorId;
|
||||||
import net.sf.briar.api.protocol.Batch;
|
import net.sf.briar.api.protocol.Batch;
|
||||||
import net.sf.briar.api.protocol.BatchBuilder;
|
|
||||||
import net.sf.briar.api.protocol.BatchId;
|
import net.sf.briar.api.protocol.BatchId;
|
||||||
import net.sf.briar.api.protocol.BundleId;
|
import net.sf.briar.api.protocol.BundleId;
|
||||||
import net.sf.briar.api.protocol.BundleReader;
|
import net.sf.briar.api.protocol.BundleReader;
|
||||||
import net.sf.briar.api.protocol.BundleWriter;
|
import net.sf.briar.api.protocol.BundleWriter;
|
||||||
import net.sf.briar.api.protocol.GroupId;
|
import net.sf.briar.api.protocol.GroupId;
|
||||||
import net.sf.briar.api.protocol.Header;
|
import net.sf.briar.api.protocol.Header;
|
||||||
import net.sf.briar.api.protocol.HeaderBuilder;
|
|
||||||
import net.sf.briar.api.protocol.Message;
|
import net.sf.briar.api.protocol.Message;
|
||||||
import net.sf.briar.api.protocol.MessageId;
|
import net.sf.briar.api.protocol.MessageId;
|
||||||
import net.sf.briar.protocol.MessageImpl;
|
import net.sf.briar.protocol.MessageImpl;
|
||||||
@@ -30,8 +28,6 @@ import org.jmock.Expectations;
|
|||||||
import org.jmock.Mockery;
|
import org.jmock.Mockery;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import com.google.inject.Provider;
|
|
||||||
|
|
||||||
public abstract class DatabaseComponentTest extends TestCase {
|
public abstract class DatabaseComponentTest extends TestCase {
|
||||||
|
|
||||||
protected final Object txn = new Object();
|
protected final Object txn = new Object();
|
||||||
@@ -73,9 +69,7 @@ public abstract class DatabaseComponentTest extends TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected abstract <T> DatabaseComponent createDatabaseComponent(
|
protected abstract <T> DatabaseComponent createDatabaseComponent(
|
||||||
Database<T> database, DatabaseCleaner cleaner,
|
Database<T> database, DatabaseCleaner cleaner);
|
||||||
Provider<HeaderBuilder> headerBuilderProvider,
|
|
||||||
Provider<BatchBuilder> batchBuilderProvider);
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSimpleCalls() throws DbException {
|
public void testSimpleCalls() throws DbException {
|
||||||
@@ -85,12 +79,6 @@ public abstract class DatabaseComponentTest extends TestCase {
|
|||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
final Database<Object> database = context.mock(Database.class);
|
final Database<Object> database = context.mock(Database.class);
|
||||||
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
final Provider<HeaderBuilder> headerBuilderProvider =
|
|
||||||
context.mock(Provider.class);
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
final Provider<BatchBuilder> batchBuilderProvider =
|
|
||||||
context.mock(Provider.class, "batchBuilderProvider");
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
allowing(database).startTransaction();
|
allowing(database).startTransaction();
|
||||||
will(returnValue(txn));
|
will(returnValue(txn));
|
||||||
@@ -129,8 +117,7 @@ public abstract class DatabaseComponentTest extends TestCase {
|
|||||||
oneOf(cleaner).stopCleaning();
|
oneOf(cleaner).stopCleaning();
|
||||||
oneOf(database).close();
|
oneOf(database).close();
|
||||||
}});
|
}});
|
||||||
DatabaseComponent db = createDatabaseComponent(database, cleaner,
|
DatabaseComponent db = createDatabaseComponent(database, cleaner);
|
||||||
headerBuilderProvider, batchBuilderProvider);
|
|
||||||
|
|
||||||
db.open(false);
|
db.open(false);
|
||||||
assertEquals(Rating.UNRATED, db.getRating(authorId));
|
assertEquals(Rating.UNRATED, db.getRating(authorId));
|
||||||
@@ -153,12 +140,6 @@ public abstract class DatabaseComponentTest extends TestCase {
|
|||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
final Database<Object> database = context.mock(Database.class);
|
final Database<Object> database = context.mock(Database.class);
|
||||||
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
final Provider<HeaderBuilder> headerBuilderProvider =
|
|
||||||
context.mock(Provider.class);
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
final Provider<BatchBuilder> batchBuilderProvider =
|
|
||||||
context.mock(Provider.class, "batchBuilderProvider");
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
// setRating(Rating.GOOD)
|
// setRating(Rating.GOOD)
|
||||||
allowing(database).startTransaction();
|
allowing(database).startTransaction();
|
||||||
@@ -175,8 +156,7 @@ public abstract class DatabaseComponentTest extends TestCase {
|
|||||||
will(returnValue(MessageId.NONE));
|
will(returnValue(MessageId.NONE));
|
||||||
oneOf(database).commitTransaction(txn);
|
oneOf(database).commitTransaction(txn);
|
||||||
}});
|
}});
|
||||||
DatabaseComponent db = createDatabaseComponent(database, cleaner,
|
DatabaseComponent db = createDatabaseComponent(database, cleaner);
|
||||||
headerBuilderProvider, batchBuilderProvider);
|
|
||||||
|
|
||||||
db.setRating(authorId, Rating.GOOD);
|
db.setRating(authorId, Rating.GOOD);
|
||||||
|
|
||||||
@@ -189,12 +169,6 @@ public abstract class DatabaseComponentTest extends TestCase {
|
|||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
final Database<Object> database = context.mock(Database.class);
|
final Database<Object> database = context.mock(Database.class);
|
||||||
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
final Provider<HeaderBuilder> headerBuilderProvider =
|
|
||||||
context.mock(Provider.class);
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
final Provider<BatchBuilder> batchBuilderProvider =
|
|
||||||
context.mock(Provider.class, "batchBuilderProvider");
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
// setRating(Rating.GOOD)
|
// setRating(Rating.GOOD)
|
||||||
oneOf(database).startTransaction();
|
oneOf(database).startTransaction();
|
||||||
@@ -214,8 +188,7 @@ public abstract class DatabaseComponentTest extends TestCase {
|
|||||||
will(returnValue(false));
|
will(returnValue(false));
|
||||||
oneOf(database).commitTransaction(txn);
|
oneOf(database).commitTransaction(txn);
|
||||||
}});
|
}});
|
||||||
DatabaseComponent db = createDatabaseComponent(database, cleaner,
|
DatabaseComponent db = createDatabaseComponent(database, cleaner);
|
||||||
headerBuilderProvider, batchBuilderProvider);
|
|
||||||
|
|
||||||
db.setRating(authorId, Rating.GOOD);
|
db.setRating(authorId, Rating.GOOD);
|
||||||
|
|
||||||
@@ -229,12 +202,6 @@ public abstract class DatabaseComponentTest extends TestCase {
|
|||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
final Database<Object> database = context.mock(Database.class);
|
final Database<Object> database = context.mock(Database.class);
|
||||||
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
final Provider<HeaderBuilder> headerBuilderProvider =
|
|
||||||
context.mock(Provider.class);
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
final Provider<BatchBuilder> batchBuilderProvider =
|
|
||||||
context.mock(Provider.class, "batchBuilderProvider");
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
// setRating(Rating.GOOD)
|
// setRating(Rating.GOOD)
|
||||||
oneOf(database).startTransaction();
|
oneOf(database).startTransaction();
|
||||||
@@ -258,8 +225,7 @@ public abstract class DatabaseComponentTest extends TestCase {
|
|||||||
will(returnValue(groupId1));
|
will(returnValue(groupId1));
|
||||||
oneOf(database).commitTransaction(txn);
|
oneOf(database).commitTransaction(txn);
|
||||||
}});
|
}});
|
||||||
DatabaseComponent db = createDatabaseComponent(database, cleaner,
|
DatabaseComponent db = createDatabaseComponent(database, cleaner);
|
||||||
headerBuilderProvider, batchBuilderProvider);
|
|
||||||
|
|
||||||
db.setRating(authorId, Rating.GOOD);
|
db.setRating(authorId, Rating.GOOD);
|
||||||
|
|
||||||
@@ -273,12 +239,6 @@ public abstract class DatabaseComponentTest extends TestCase {
|
|||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
final Database<Object> database = context.mock(Database.class);
|
final Database<Object> database = context.mock(Database.class);
|
||||||
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
final Provider<HeaderBuilder> headerBuilderProvider =
|
|
||||||
context.mock(Provider.class);
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
final Provider<BatchBuilder> batchBuilderProvider =
|
|
||||||
context.mock(Provider.class, "batchBuilderProvider");
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
// setRating(Rating.GOOD)
|
// setRating(Rating.GOOD)
|
||||||
oneOf(database).startTransaction();
|
oneOf(database).startTransaction();
|
||||||
@@ -305,8 +265,7 @@ public abstract class DatabaseComponentTest extends TestCase {
|
|||||||
oneOf(database).setSendability(txn, parentId, 2);
|
oneOf(database).setSendability(txn, parentId, 2);
|
||||||
oneOf(database).commitTransaction(txn);
|
oneOf(database).commitTransaction(txn);
|
||||||
}});
|
}});
|
||||||
DatabaseComponent db = createDatabaseComponent(database, cleaner,
|
DatabaseComponent db = createDatabaseComponent(database, cleaner);
|
||||||
headerBuilderProvider, batchBuilderProvider);
|
|
||||||
|
|
||||||
db.setRating(authorId, Rating.GOOD);
|
db.setRating(authorId, Rating.GOOD);
|
||||||
|
|
||||||
@@ -320,12 +279,6 @@ public abstract class DatabaseComponentTest extends TestCase {
|
|||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
final Database<Object> database = context.mock(Database.class);
|
final Database<Object> database = context.mock(Database.class);
|
||||||
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
final Provider<HeaderBuilder> headerBuilderProvider =
|
|
||||||
context.mock(Provider.class);
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
final Provider<BatchBuilder> batchBuilderProvider =
|
|
||||||
context.mock(Provider.class, "batchBuilderProvider");
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
// setRating(Rating.GOOD)
|
// setRating(Rating.GOOD)
|
||||||
oneOf(database).startTransaction();
|
oneOf(database).startTransaction();
|
||||||
@@ -354,8 +307,7 @@ public abstract class DatabaseComponentTest extends TestCase {
|
|||||||
will(returnValue(MessageId.NONE));
|
will(returnValue(MessageId.NONE));
|
||||||
oneOf(database).commitTransaction(txn);
|
oneOf(database).commitTransaction(txn);
|
||||||
}});
|
}});
|
||||||
DatabaseComponent db = createDatabaseComponent(database, cleaner,
|
DatabaseComponent db = createDatabaseComponent(database, cleaner);
|
||||||
headerBuilderProvider, batchBuilderProvider);
|
|
||||||
|
|
||||||
db.setRating(authorId, Rating.GOOD);
|
db.setRating(authorId, Rating.GOOD);
|
||||||
|
|
||||||
@@ -369,12 +321,6 @@ public abstract class DatabaseComponentTest extends TestCase {
|
|||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
final Database<Object> database = context.mock(Database.class);
|
final Database<Object> database = context.mock(Database.class);
|
||||||
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
final Provider<HeaderBuilder> headerBuilderProvider =
|
|
||||||
context.mock(Provider.class);
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
final Provider<BatchBuilder> batchBuilderProvider =
|
|
||||||
context.mock(Provider.class, "batchBuilderProvider");
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
// addLocallyGeneratedMessage(message)
|
// addLocallyGeneratedMessage(message)
|
||||||
oneOf(database).startTransaction();
|
oneOf(database).startTransaction();
|
||||||
@@ -383,8 +329,7 @@ public abstract class DatabaseComponentTest extends TestCase {
|
|||||||
will(returnValue(false));
|
will(returnValue(false));
|
||||||
oneOf(database).commitTransaction(txn);
|
oneOf(database).commitTransaction(txn);
|
||||||
}});
|
}});
|
||||||
DatabaseComponent db = createDatabaseComponent(database, cleaner,
|
DatabaseComponent db = createDatabaseComponent(database, cleaner);
|
||||||
headerBuilderProvider, batchBuilderProvider);
|
|
||||||
|
|
||||||
db.addLocallyGeneratedMessage(message);
|
db.addLocallyGeneratedMessage(message);
|
||||||
|
|
||||||
@@ -397,12 +342,6 @@ public abstract class DatabaseComponentTest extends TestCase {
|
|||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
final Database<Object> database = context.mock(Database.class);
|
final Database<Object> database = context.mock(Database.class);
|
||||||
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
final Provider<HeaderBuilder> headerBuilderProvider =
|
|
||||||
context.mock(Provider.class);
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
final Provider<BatchBuilder> batchBuilderProvider =
|
|
||||||
context.mock(Provider.class, "batchBuilderProvider");
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
// addLocallyGeneratedMessage(message)
|
// addLocallyGeneratedMessage(message)
|
||||||
oneOf(database).startTransaction();
|
oneOf(database).startTransaction();
|
||||||
@@ -413,8 +352,7 @@ public abstract class DatabaseComponentTest extends TestCase {
|
|||||||
will(returnValue(false));
|
will(returnValue(false));
|
||||||
oneOf(database).commitTransaction(txn);
|
oneOf(database).commitTransaction(txn);
|
||||||
}});
|
}});
|
||||||
DatabaseComponent db = createDatabaseComponent(database, cleaner,
|
DatabaseComponent db = createDatabaseComponent(database, cleaner);
|
||||||
headerBuilderProvider, batchBuilderProvider);
|
|
||||||
|
|
||||||
db.addLocallyGeneratedMessage(message);
|
db.addLocallyGeneratedMessage(message);
|
||||||
|
|
||||||
@@ -427,12 +365,6 @@ public abstract class DatabaseComponentTest extends TestCase {
|
|||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
final Database<Object> database = context.mock(Database.class);
|
final Database<Object> database = context.mock(Database.class);
|
||||||
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
final Provider<HeaderBuilder> headerBuilderProvider =
|
|
||||||
context.mock(Provider.class);
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
final Provider<BatchBuilder> batchBuilderProvider =
|
|
||||||
context.mock(Provider.class, "batchBuilderProvider");
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
// addLocallyGeneratedMessage(message)
|
// addLocallyGeneratedMessage(message)
|
||||||
oneOf(database).startTransaction();
|
oneOf(database).startTransaction();
|
||||||
@@ -452,8 +384,7 @@ public abstract class DatabaseComponentTest extends TestCase {
|
|||||||
oneOf(database).setSendability(txn, messageId, 0);
|
oneOf(database).setSendability(txn, messageId, 0);
|
||||||
oneOf(database).commitTransaction(txn);
|
oneOf(database).commitTransaction(txn);
|
||||||
}});
|
}});
|
||||||
DatabaseComponent db = createDatabaseComponent(database, cleaner,
|
DatabaseComponent db = createDatabaseComponent(database, cleaner);
|
||||||
headerBuilderProvider, batchBuilderProvider);
|
|
||||||
|
|
||||||
db.addLocallyGeneratedMessage(message);
|
db.addLocallyGeneratedMessage(message);
|
||||||
|
|
||||||
@@ -467,12 +398,6 @@ public abstract class DatabaseComponentTest extends TestCase {
|
|||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
final Database<Object> database = context.mock(Database.class);
|
final Database<Object> database = context.mock(Database.class);
|
||||||
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
final Provider<HeaderBuilder> headerBuilderProvider =
|
|
||||||
context.mock(Provider.class);
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
final Provider<BatchBuilder> batchBuilderProvider =
|
|
||||||
context.mock(Provider.class, "batchBuilderProvider");
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
// addLocallyGeneratedMessage(message)
|
// addLocallyGeneratedMessage(message)
|
||||||
oneOf(database).startTransaction();
|
oneOf(database).startTransaction();
|
||||||
@@ -495,8 +420,7 @@ public abstract class DatabaseComponentTest extends TestCase {
|
|||||||
will(returnValue(MessageId.NONE));
|
will(returnValue(MessageId.NONE));
|
||||||
oneOf(database).commitTransaction(txn);
|
oneOf(database).commitTransaction(txn);
|
||||||
}});
|
}});
|
||||||
DatabaseComponent db = createDatabaseComponent(database, cleaner,
|
DatabaseComponent db = createDatabaseComponent(database, cleaner);
|
||||||
headerBuilderProvider, batchBuilderProvider);
|
|
||||||
|
|
||||||
db.addLocallyGeneratedMessage(message);
|
db.addLocallyGeneratedMessage(message);
|
||||||
|
|
||||||
@@ -510,12 +434,6 @@ public abstract class DatabaseComponentTest extends TestCase {
|
|||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
final Database<Object> database = context.mock(Database.class);
|
final Database<Object> database = context.mock(Database.class);
|
||||||
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
final Provider<HeaderBuilder> headerBuilderProvider =
|
|
||||||
context.mock(Provider.class);
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
final Provider<BatchBuilder> batchBuilderProvider =
|
|
||||||
context.mock(Provider.class, "batchBuilderProvider");
|
|
||||||
final BundleWriter bundleBuilder = context.mock(BundleWriter.class);
|
final BundleWriter bundleBuilder = context.mock(BundleWriter.class);
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
// Check that the contact is still in the DB
|
// Check that the contact is still in the DB
|
||||||
@@ -525,8 +443,7 @@ public abstract class DatabaseComponentTest extends TestCase {
|
|||||||
will(returnValue(false));
|
will(returnValue(false));
|
||||||
oneOf(database).commitTransaction(txn);
|
oneOf(database).commitTransaction(txn);
|
||||||
}});
|
}});
|
||||||
DatabaseComponent db = createDatabaseComponent(database, cleaner,
|
DatabaseComponent db = createDatabaseComponent(database, cleaner);
|
||||||
headerBuilderProvider, batchBuilderProvider);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
db.generateBundle(contactId, bundleBuilder);
|
db.generateBundle(contactId, bundleBuilder);
|
||||||
@@ -543,72 +460,43 @@ public abstract class DatabaseComponentTest extends TestCase {
|
|||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
final Database<Object> database = context.mock(Database.class);
|
final Database<Object> database = context.mock(Database.class);
|
||||||
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
final Provider<HeaderBuilder> headerBuilderProvider =
|
|
||||||
context.mock(Provider.class);
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
final Provider<BatchBuilder> batchBuilderProvider =
|
|
||||||
context.mock(Provider.class, "batchBuilderProvider");
|
|
||||||
final BundleWriter bundleWriter = context.mock(BundleWriter.class);
|
final BundleWriter bundleWriter = context.mock(BundleWriter.class);
|
||||||
final HeaderBuilder headerBuilder = context.mock(HeaderBuilder.class);
|
|
||||||
final Header header = context.mock(Header.class);
|
|
||||||
final BatchBuilder batchBuilder = context.mock(BatchBuilder.class);
|
|
||||||
final Batch batch = context.mock(Batch.class);
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
allowing(database).startTransaction();
|
allowing(database).startTransaction();
|
||||||
will(returnValue(txn));
|
will(returnValue(txn));
|
||||||
allowing(database).commitTransaction(txn);
|
allowing(database).commitTransaction(txn);
|
||||||
allowing(database).containsContact(txn, contactId);
|
allowing(database).containsContact(txn, contactId);
|
||||||
will(returnValue(true));
|
will(returnValue(true));
|
||||||
// Build the header
|
|
||||||
oneOf(headerBuilderProvider).get();
|
|
||||||
will(returnValue(headerBuilder));
|
|
||||||
// Add acks to the header
|
// Add acks to the header
|
||||||
oneOf(database).removeBatchesToAck(txn, contactId);
|
oneOf(database).removeBatchesToAck(txn, contactId);
|
||||||
will(returnValue(acks));
|
will(returnValue(acks));
|
||||||
oneOf(headerBuilder).addAcks(acks);
|
|
||||||
// Add subscriptions to the header
|
// Add subscriptions to the header
|
||||||
oneOf(database).getSubscriptions(txn);
|
oneOf(database).getSubscriptions(txn);
|
||||||
will(returnValue(subs));
|
will(returnValue(subs));
|
||||||
oneOf(headerBuilder).addSubscriptions(subs);
|
|
||||||
// Add transports to the header
|
// Add transports to the header
|
||||||
oneOf(database).getTransports(txn);
|
oneOf(database).getTransports(txn);
|
||||||
will(returnValue(transports));
|
will(returnValue(transports));
|
||||||
oneOf(headerBuilder).addTransports(transports);
|
|
||||||
// Build the header
|
// Build the header
|
||||||
oneOf(headerBuilder).build();
|
oneOf(bundleWriter).addHeader(acks, subs, transports);
|
||||||
will(returnValue(header));
|
will(returnValue(bundleId));
|
||||||
oneOf(bundleWriter).getCapacity();
|
|
||||||
will(returnValue(1024L * 1024L));
|
|
||||||
oneOf(header).getSize();
|
|
||||||
will(returnValue(headerSize));
|
|
||||||
oneOf(bundleWriter).addHeader(header);
|
|
||||||
// Add a batch to the bundle
|
// Add a batch to the bundle
|
||||||
|
oneOf(bundleWriter).getRemainingCapacity();
|
||||||
|
will(returnValue(1024L * 1024L - headerSize));
|
||||||
oneOf(database).getSendableMessages(txn, contactId,
|
oneOf(database).getSendableMessages(txn, contactId,
|
||||||
Batch.MAX_SIZE - headerSize);
|
Batch.MAX_SIZE - headerSize);
|
||||||
will(returnValue(messages));
|
will(returnValue(messages));
|
||||||
oneOf(batchBuilderProvider).get();
|
|
||||||
will(returnValue(batchBuilder));
|
|
||||||
oneOf(database).getMessage(txn, messageId);
|
oneOf(database).getMessage(txn, messageId);
|
||||||
will(returnValue(message));
|
will(returnValue(message));
|
||||||
oneOf(batchBuilder).addMessage(message);
|
// Add the batch to the bundle
|
||||||
oneOf(batchBuilder).build();
|
oneOf(bundleWriter).addBatch(Collections.singletonList(message));
|
||||||
will(returnValue(batch));
|
|
||||||
// Record the batch as outstanding
|
|
||||||
oneOf(batch).getId();
|
|
||||||
will(returnValue(batchId));
|
will(returnValue(batchId));
|
||||||
|
// Record the outstanding batch
|
||||||
oneOf(database).addOutstandingBatch(
|
oneOf(database).addOutstandingBatch(
|
||||||
txn, contactId, batchId, messages);
|
txn, contactId, batchId, messages);
|
||||||
// Add the batch to the bundle
|
// Send the bundle
|
||||||
oneOf(bundleWriter).addBatch(batch);
|
oneOf(bundleWriter).finish();
|
||||||
// Check whether to add another batch
|
|
||||||
oneOf(batch).getSize();
|
|
||||||
will(returnValue((long) message.getSize()));
|
|
||||||
// No, just send the bundle
|
|
||||||
oneOf(bundleWriter).close();
|
|
||||||
}});
|
}});
|
||||||
DatabaseComponent db = createDatabaseComponent(database, cleaner,
|
DatabaseComponent db = createDatabaseComponent(database, cleaner);
|
||||||
headerBuilderProvider, batchBuilderProvider);
|
|
||||||
|
|
||||||
db.generateBundle(contactId, bundleWriter);
|
db.generateBundle(contactId, bundleWriter);
|
||||||
|
|
||||||
@@ -622,12 +510,6 @@ public abstract class DatabaseComponentTest extends TestCase {
|
|||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
final Database<Object> database = context.mock(Database.class);
|
final Database<Object> database = context.mock(Database.class);
|
||||||
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
final Provider<HeaderBuilder> headerBuilderProvider =
|
|
||||||
context.mock(Provider.class);
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
final Provider<BatchBuilder> batchBuilderProvider =
|
|
||||||
context.mock(Provider.class, "batchBuilderProvider");
|
|
||||||
final BundleReader bundleReader = context.mock(BundleReader.class);
|
final BundleReader bundleReader = context.mock(BundleReader.class);
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
// Check that the contact is still in the DB
|
// Check that the contact is still in the DB
|
||||||
@@ -637,8 +519,7 @@ public abstract class DatabaseComponentTest extends TestCase {
|
|||||||
will(returnValue(false));
|
will(returnValue(false));
|
||||||
oneOf(database).commitTransaction(txn);
|
oneOf(database).commitTransaction(txn);
|
||||||
}});
|
}});
|
||||||
DatabaseComponent db = createDatabaseComponent(database, cleaner,
|
DatabaseComponent db = createDatabaseComponent(database, cleaner);
|
||||||
headerBuilderProvider, batchBuilderProvider);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
db.receiveBundle(contactId, bundleReader);
|
db.receiveBundle(contactId, bundleReader);
|
||||||
@@ -654,12 +535,6 @@ public abstract class DatabaseComponentTest extends TestCase {
|
|||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
final Database<Object> database = context.mock(Database.class);
|
final Database<Object> database = context.mock(Database.class);
|
||||||
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
final Provider<HeaderBuilder> headerBuilderProvider =
|
|
||||||
context.mock(Provider.class);
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
final Provider<BatchBuilder> batchBuilderProvider =
|
|
||||||
context.mock(Provider.class, "batchBuilderProvider");
|
|
||||||
final BundleReader bundleReader = context.mock(BundleReader.class);
|
final BundleReader bundleReader = context.mock(BundleReader.class);
|
||||||
final Header header = context.mock(Header.class);
|
final Header header = context.mock(Header.class);
|
||||||
final Batch batch = context.mock(Batch.class);
|
final Batch batch = context.mock(Batch.class);
|
||||||
@@ -702,7 +577,7 @@ public abstract class DatabaseComponentTest extends TestCase {
|
|||||||
// Any more batches? Nope
|
// Any more batches? Nope
|
||||||
oneOf(bundleReader).getNextBatch();
|
oneOf(bundleReader).getNextBatch();
|
||||||
will(returnValue(null));
|
will(returnValue(null));
|
||||||
oneOf(bundleReader).close();
|
oneOf(bundleReader).finish();
|
||||||
// Lost batches
|
// Lost batches
|
||||||
oneOf(header).getId();
|
oneOf(header).getId();
|
||||||
will(returnValue(bundleId));
|
will(returnValue(bundleId));
|
||||||
@@ -710,8 +585,7 @@ public abstract class DatabaseComponentTest extends TestCase {
|
|||||||
will(returnValue(Collections.singleton(batchId)));
|
will(returnValue(Collections.singleton(batchId)));
|
||||||
oneOf(database).removeLostBatch(txn, contactId, batchId);
|
oneOf(database).removeLostBatch(txn, contactId, batchId);
|
||||||
}});
|
}});
|
||||||
DatabaseComponent db = createDatabaseComponent(database, cleaner,
|
DatabaseComponent db = createDatabaseComponent(database, cleaner);
|
||||||
headerBuilderProvider, batchBuilderProvider);
|
|
||||||
|
|
||||||
db.receiveBundle(contactId, bundleReader);
|
db.receiveBundle(contactId, bundleReader);
|
||||||
|
|
||||||
|
|||||||
@@ -815,8 +815,8 @@ public class H2DatabaseTest extends TestCase {
|
|||||||
private static class TestMessageFactory implements MessageFactory {
|
private static class TestMessageFactory implements MessageFactory {
|
||||||
|
|
||||||
public Message createMessage(MessageId id, MessageId parent,
|
public Message createMessage(MessageId id, MessageId parent,
|
||||||
GroupId group, AuthorId author, long timestamp, byte[] body) {
|
GroupId group, AuthorId author, long timestamp, byte[] raw) {
|
||||||
return new MessageImpl(id, parent, group, author, timestamp, body);
|
return new MessageImpl(id, parent, group, author, timestamp, raw);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,29 +1,19 @@
|
|||||||
package net.sf.briar.db;
|
package net.sf.briar.db;
|
||||||
|
|
||||||
import net.sf.briar.api.db.DatabaseComponent;
|
import net.sf.briar.api.db.DatabaseComponent;
|
||||||
import net.sf.briar.api.protocol.BatchBuilder;
|
|
||||||
import net.sf.briar.api.protocol.HeaderBuilder;
|
|
||||||
|
|
||||||
import com.google.inject.Provider;
|
|
||||||
|
|
||||||
public class ReadWriteLockDatabaseComponentTest
|
public class ReadWriteLockDatabaseComponentTest
|
||||||
extends DatabaseComponentImplTest {
|
extends DatabaseComponentImplTest {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected <T> DatabaseComponent createDatabaseComponent(
|
protected <T> DatabaseComponent createDatabaseComponent(
|
||||||
Database<T> database, DatabaseCleaner cleaner,
|
Database<T> database, DatabaseCleaner cleaner) {
|
||||||
Provider<HeaderBuilder> headerBuilderProvider,
|
return createDatabaseComponentImpl(database, cleaner);
|
||||||
Provider<BatchBuilder> batchBuilderProvider) {
|
|
||||||
return createDatabaseComponentImpl(database, cleaner,
|
|
||||||
headerBuilderProvider, batchBuilderProvider);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected <T> DatabaseComponentImpl<T> createDatabaseComponentImpl(
|
protected <T> DatabaseComponentImpl<T> createDatabaseComponentImpl(
|
||||||
Database<T> database, DatabaseCleaner cleaner,
|
Database<T> database, DatabaseCleaner cleaner) {
|
||||||
Provider<HeaderBuilder> headerBuilderProvider,
|
return new ReadWriteLockDatabaseComponent<T>(database, cleaner);
|
||||||
Provider<BatchBuilder> batchBuilderProvider) {
|
|
||||||
return new ReadWriteLockDatabaseComponent<T>(database, cleaner,
|
|
||||||
headerBuilderProvider, batchBuilderProvider);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,29 +1,19 @@
|
|||||||
package net.sf.briar.db;
|
package net.sf.briar.db;
|
||||||
|
|
||||||
import net.sf.briar.api.db.DatabaseComponent;
|
import net.sf.briar.api.db.DatabaseComponent;
|
||||||
import net.sf.briar.api.protocol.BatchBuilder;
|
|
||||||
import net.sf.briar.api.protocol.HeaderBuilder;
|
|
||||||
|
|
||||||
import com.google.inject.Provider;
|
|
||||||
|
|
||||||
public class SynchronizedDatabaseComponentTest
|
public class SynchronizedDatabaseComponentTest
|
||||||
extends DatabaseComponentImplTest {
|
extends DatabaseComponentImplTest {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected <T> DatabaseComponent createDatabaseComponent(
|
protected <T> DatabaseComponent createDatabaseComponent(
|
||||||
Database<T> database, DatabaseCleaner cleaner,
|
Database<T> database, DatabaseCleaner cleaner) {
|
||||||
Provider<HeaderBuilder> headerBuilderProvider,
|
return createDatabaseComponentImpl(database, cleaner);
|
||||||
Provider<BatchBuilder> batchBuilderProvider) {
|
|
||||||
return createDatabaseComponentImpl(database, cleaner,
|
|
||||||
headerBuilderProvider, batchBuilderProvider);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected <T> DatabaseComponentImpl<T> createDatabaseComponentImpl(
|
protected <T> DatabaseComponentImpl<T> createDatabaseComponentImpl(
|
||||||
Database<T> database, DatabaseCleaner cleaner,
|
Database<T> database, DatabaseCleaner cleaner) {
|
||||||
Provider<HeaderBuilder> headerBuilderProvider,
|
return new SynchronizedDatabaseComponent<T>(database, cleaner);
|
||||||
Provider<BatchBuilder> batchBuilderProvider) {
|
|
||||||
return new SynchronizedDatabaseComponent<T>(database, cleaner,
|
|
||||||
headerBuilderProvider, batchBuilderProvider);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,21 +24,17 @@ import junit.framework.TestCase;
|
|||||||
import net.sf.briar.TestUtils;
|
import net.sf.briar.TestUtils;
|
||||||
import net.sf.briar.api.crypto.KeyParser;
|
import net.sf.briar.api.crypto.KeyParser;
|
||||||
import net.sf.briar.api.protocol.Batch;
|
import net.sf.briar.api.protocol.Batch;
|
||||||
import net.sf.briar.api.protocol.BatchBuilder;
|
|
||||||
import net.sf.briar.api.protocol.BatchId;
|
import net.sf.briar.api.protocol.BatchId;
|
||||||
import net.sf.briar.api.protocol.BundleReader;
|
import net.sf.briar.api.protocol.BundleReader;
|
||||||
import net.sf.briar.api.protocol.BundleWriter;
|
import net.sf.briar.api.protocol.BundleWriter;
|
||||||
import net.sf.briar.api.protocol.GroupId;
|
import net.sf.briar.api.protocol.GroupId;
|
||||||
import net.sf.briar.api.protocol.Header;
|
import net.sf.briar.api.protocol.Header;
|
||||||
import net.sf.briar.api.protocol.HeaderBuilder;
|
|
||||||
import net.sf.briar.api.protocol.Message;
|
import net.sf.briar.api.protocol.Message;
|
||||||
import net.sf.briar.api.protocol.MessageEncoder;
|
import net.sf.briar.api.protocol.MessageEncoder;
|
||||||
import net.sf.briar.api.protocol.MessageId;
|
import net.sf.briar.api.protocol.MessageId;
|
||||||
import net.sf.briar.api.protocol.MessageParser;
|
import net.sf.briar.api.protocol.MessageParser;
|
||||||
import net.sf.briar.api.protocol.UniqueId;
|
import net.sf.briar.api.protocol.UniqueId;
|
||||||
import net.sf.briar.api.serial.Reader;
|
|
||||||
import net.sf.briar.api.serial.ReaderFactory;
|
import net.sf.briar.api.serial.ReaderFactory;
|
||||||
import net.sf.briar.api.serial.Writer;
|
|
||||||
import net.sf.briar.api.serial.WriterFactory;
|
import net.sf.briar.api.serial.WriterFactory;
|
||||||
import net.sf.briar.serial.ReaderFactoryImpl;
|
import net.sf.briar.serial.ReaderFactoryImpl;
|
||||||
import net.sf.briar.serial.WriterFactoryImpl;
|
import net.sf.briar.serial.WriterFactoryImpl;
|
||||||
@@ -47,8 +43,6 @@ import org.junit.After;
|
|||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import com.google.inject.Provider;
|
|
||||||
|
|
||||||
public class BundleReadWriteTest extends TestCase {
|
public class BundleReadWriteTest extends TestCase {
|
||||||
|
|
||||||
private static final String SIGNATURE_ALGO = "SHA256withRSA";
|
private static final String SIGNATURE_ALGO = "SHA256withRSA";
|
||||||
@@ -74,7 +68,7 @@ public class BundleReadWriteTest extends TestCase {
|
|||||||
|
|
||||||
private final KeyPair keyPair;
|
private final KeyPair keyPair;
|
||||||
private final Signature sig;
|
private final Signature sig;
|
||||||
private final MessageDigest digest;
|
private final MessageDigest dig;
|
||||||
private final KeyParser keyParser;
|
private final KeyParser keyParser;
|
||||||
private final Message message;
|
private final Message message;
|
||||||
|
|
||||||
@@ -82,7 +76,7 @@ public class BundleReadWriteTest extends TestCase {
|
|||||||
super();
|
super();
|
||||||
keyPair = KeyPairGenerator.getInstance(KEY_PAIR_ALGO).generateKeyPair();
|
keyPair = KeyPairGenerator.getInstance(KEY_PAIR_ALGO).generateKeyPair();
|
||||||
sig = Signature.getInstance(SIGNATURE_ALGO);
|
sig = Signature.getInstance(SIGNATURE_ALGO);
|
||||||
digest = MessageDigest.getInstance(DIGEST_ALGO);
|
dig = MessageDigest.getInstance(DIGEST_ALGO);
|
||||||
final KeyFactory keyFactory = KeyFactory.getInstance(KEY_PAIR_ALGO);
|
final KeyFactory keyFactory = KeyFactory.getInstance(KEY_PAIR_ALGO);
|
||||||
keyParser = new KeyParser() {
|
keyParser = new KeyParser() {
|
||||||
public PublicKey parsePublicKey(byte[] encodedKey)
|
public PublicKey parsePublicKey(byte[] encodedKey)
|
||||||
@@ -91,8 +85,8 @@ public class BundleReadWriteTest extends TestCase {
|
|||||||
return keyFactory.generatePublic(e);
|
return keyFactory.generatePublic(e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
assertEquals(digest.getDigestLength(), UniqueId.LENGTH);
|
assertEquals(dig.getDigestLength(), UniqueId.LENGTH);
|
||||||
MessageEncoder messageEncoder = new MessageEncoderImpl(sig, digest, wf);
|
MessageEncoder messageEncoder = new MessageEncoderImpl(sig, dig, wf);
|
||||||
message = messageEncoder.encodeMessage(MessageId.NONE, sub, nick,
|
message = messageEncoder.encodeMessage(MessageId.NONE, sub, nick,
|
||||||
keyPair, messageBody.getBytes("UTF-8"));
|
keyPair, messageBody.getBytes("UTF-8"));
|
||||||
}
|
}
|
||||||
@@ -104,23 +98,13 @@ public class BundleReadWriteTest extends TestCase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testWriteBundle() throws Exception {
|
public void testWriteBundle() throws Exception {
|
||||||
HeaderBuilder h = new OutgoingHeaderBuilder(keyPair, sig, digest, wf);
|
|
||||||
h.addAcks(acks);
|
|
||||||
h.addSubscriptions(subs);
|
|
||||||
h.addTransports(transports);
|
|
||||||
Header header = h.build();
|
|
||||||
|
|
||||||
BatchBuilder b = new OutgoingBatchBuilder(keyPair, sig, digest, wf);
|
|
||||||
b.addMessage(message);
|
|
||||||
Batch batch = b.build();
|
|
||||||
|
|
||||||
FileOutputStream out = new FileOutputStream(bundle);
|
FileOutputStream out = new FileOutputStream(bundle);
|
||||||
Writer writer = new WriterFactoryImpl().createWriter(out);
|
BundleWriter w = new BundleWriterImpl(out, wf, keyPair.getPrivate(),
|
||||||
BundleWriter w = new BundleWriterImpl(writer, capacity);
|
sig, dig, capacity);
|
||||||
|
|
||||||
w.addHeader(header);
|
w.addHeader(acks, subs, transports);
|
||||||
w.addBatch(batch);
|
w.addBatch(Collections.singleton(message));
|
||||||
w.close();
|
w.finish();
|
||||||
|
|
||||||
assertTrue(bundle.exists());
|
assertTrue(bundle.exists());
|
||||||
assertTrue(bundle.length() > message.getSize());
|
assertTrue(bundle.length() > message.getSize());
|
||||||
@@ -132,24 +116,11 @@ public class BundleReadWriteTest extends TestCase {
|
|||||||
testWriteBundle();
|
testWriteBundle();
|
||||||
|
|
||||||
MessageParser messageParser =
|
MessageParser messageParser =
|
||||||
new MessageParserImpl(keyParser, sig, digest, rf);
|
new MessageParserImpl(keyParser, sig, dig, rf);
|
||||||
Provider<HeaderBuilder> headerBuilderProvider =
|
|
||||||
new Provider<HeaderBuilder>() {
|
|
||||||
public HeaderBuilder get() {
|
|
||||||
return new IncomingHeaderBuilder(keyPair, sig, digest, wf);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Provider<BatchBuilder> batchBuilderProvider =
|
|
||||||
new Provider<BatchBuilder>() {
|
|
||||||
public BatchBuilder get() {
|
|
||||||
return new IncomingBatchBuilder(keyPair, sig, digest, wf);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
FileInputStream in = new FileInputStream(bundle);
|
FileInputStream in = new FileInputStream(bundle);
|
||||||
Reader reader = new ReaderFactoryImpl().createReader(in);
|
BundleReader r = new BundleReaderImpl(in, rf, keyPair.getPublic(), sig,
|
||||||
BundleReader r = new BundleReaderImpl(reader, bundle.length(),
|
dig, messageParser, new HeaderFactoryImpl(),
|
||||||
messageParser, headerBuilderProvider, batchBuilderProvider);
|
new BatchFactoryImpl());
|
||||||
|
|
||||||
Header h = r.getHeader();
|
Header h = r.getHeader();
|
||||||
assertEquals(acks, h.getAcks());
|
assertEquals(acks, h.getAcks());
|
||||||
@@ -167,7 +138,7 @@ public class BundleReadWriteTest extends TestCase {
|
|||||||
assertTrue(Arrays.equals(message.getBytes(), m.getBytes()));
|
assertTrue(Arrays.equals(message.getBytes(), m.getBytes()));
|
||||||
assertFalse(i.hasNext());
|
assertFalse(i.hasNext());
|
||||||
assertNull(r.getNextBatch());
|
assertNull(r.getNextBatch());
|
||||||
r.close();
|
r.finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -184,24 +155,11 @@ public class BundleReadWriteTest extends TestCase {
|
|||||||
f.close();
|
f.close();
|
||||||
|
|
||||||
MessageParser messageParser =
|
MessageParser messageParser =
|
||||||
new MessageParserImpl(keyParser, sig, digest, rf);
|
new MessageParserImpl(keyParser, sig, dig, rf);
|
||||||
Provider<HeaderBuilder> headerBuilderProvider =
|
|
||||||
new Provider<HeaderBuilder>() {
|
|
||||||
public HeaderBuilder get() {
|
|
||||||
return new IncomingHeaderBuilder(keyPair, sig, digest, wf);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Provider<BatchBuilder> batchBuilderProvider =
|
|
||||||
new Provider<BatchBuilder>() {
|
|
||||||
public BatchBuilder get() {
|
|
||||||
return new IncomingBatchBuilder(keyPair, sig, digest, wf);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
FileInputStream in = new FileInputStream(bundle);
|
FileInputStream in = new FileInputStream(bundle);
|
||||||
Reader reader = new ReaderFactoryImpl().createReader(in);
|
BundleReader r = new BundleReaderImpl(in, rf, keyPair.getPublic(), sig,
|
||||||
BundleReader r = new BundleReaderImpl(reader, bundle.length(),
|
dig, messageParser, new HeaderFactoryImpl(),
|
||||||
messageParser, headerBuilderProvider, batchBuilderProvider);
|
new BatchFactoryImpl());
|
||||||
|
|
||||||
Header h = r.getHeader();
|
Header h = r.getHeader();
|
||||||
assertEquals(acks, h.getAcks());
|
assertEquals(acks, h.getAcks());
|
||||||
@@ -211,7 +169,7 @@ public class BundleReadWriteTest extends TestCase {
|
|||||||
r.getNextBatch();
|
r.getNextBatch();
|
||||||
assertTrue(false);
|
assertTrue(false);
|
||||||
} catch(GeneralSecurityException expected) {}
|
} catch(GeneralSecurityException expected) {}
|
||||||
r.close();
|
r.finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
|
|||||||
@@ -1,266 +0,0 @@
|
|||||||
package net.sf.briar.protocol;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.security.SignatureException;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
|
||||||
import net.sf.briar.TestUtils;
|
|
||||||
import net.sf.briar.api.protocol.Batch;
|
|
||||||
import net.sf.briar.api.protocol.BatchBuilder;
|
|
||||||
import net.sf.briar.api.protocol.BatchId;
|
|
||||||
import net.sf.briar.api.protocol.BundleReader;
|
|
||||||
import net.sf.briar.api.protocol.GroupId;
|
|
||||||
import net.sf.briar.api.protocol.Header;
|
|
||||||
import net.sf.briar.api.protocol.HeaderBuilder;
|
|
||||||
import net.sf.briar.api.protocol.Message;
|
|
||||||
import net.sf.briar.api.protocol.MessageParser;
|
|
||||||
import net.sf.briar.api.serial.Raw;
|
|
||||||
import net.sf.briar.api.serial.Reader;
|
|
||||||
|
|
||||||
import org.jmock.Expectations;
|
|
||||||
import org.jmock.Mockery;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import com.google.inject.Provider;
|
|
||||||
|
|
||||||
public class BundleReaderTest extends TestCase {
|
|
||||||
|
|
||||||
private final long size = 1024L * 1024L;
|
|
||||||
private final BatchId ack = new BatchId(TestUtils.getRandomId());
|
|
||||||
private final List<Raw> rawAcks =
|
|
||||||
Collections.<Raw>singletonList(new TestRaw(ack.getBytes()));
|
|
||||||
private final Set<BatchId> acks = Collections.singleton(ack);
|
|
||||||
private final GroupId sub = new GroupId(TestUtils.getRandomId());
|
|
||||||
private final List<Raw> rawSubs =
|
|
||||||
Collections.<Raw>singletonList(new TestRaw(sub.getBytes()));
|
|
||||||
private final Set<GroupId> subs = Collections.singleton(sub);
|
|
||||||
private final Map<String, String> transports =
|
|
||||||
Collections.singletonMap("foo", "bar");
|
|
||||||
private final byte[] headerSig = TestUtils.getRandomId();
|
|
||||||
private final byte[] messageBody = new byte[123];
|
|
||||||
private final List<Raw> rawMessages =
|
|
||||||
Collections.<Raw>singletonList(new TestRaw(messageBody));
|
|
||||||
private final byte[] batchSig = TestUtils.getRandomId();
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testGetHeader() throws Exception {
|
|
||||||
Mockery context = new Mockery();
|
|
||||||
final Reader reader = context.mock(Reader.class);
|
|
||||||
final MessageParser messageParser = context.mock(MessageParser.class);
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
final Provider<HeaderBuilder> headerBuilderProvider =
|
|
||||||
context.mock(Provider.class);
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
final Provider<BatchBuilder> batchBuilderProvider =
|
|
||||||
context.mock(Provider.class, "batchBuilderProvider");
|
|
||||||
final HeaderBuilder headerBuilder = context.mock(HeaderBuilder.class);
|
|
||||||
final Header header = context.mock(Header.class);
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
oneOf(reader).setReadLimit(Header.MAX_SIZE);
|
|
||||||
oneOf(headerBuilderProvider).get();
|
|
||||||
will(returnValue(headerBuilder));
|
|
||||||
// Acks
|
|
||||||
oneOf(reader).readList(Raw.class);
|
|
||||||
will(returnValue(rawAcks));
|
|
||||||
oneOf(headerBuilder).addAcks(acks);
|
|
||||||
// Subs
|
|
||||||
oneOf(reader).readList(Raw.class);
|
|
||||||
will(returnValue(rawSubs));
|
|
||||||
oneOf(headerBuilder).addSubscriptions(subs);
|
|
||||||
// Transports
|
|
||||||
oneOf(reader).readMap(String.class, String.class);
|
|
||||||
will(returnValue(transports));
|
|
||||||
oneOf(headerBuilder).addTransports(transports);
|
|
||||||
// Signature
|
|
||||||
oneOf(reader).readRaw();
|
|
||||||
will(returnValue(headerSig));
|
|
||||||
oneOf(headerBuilder).setSignature(headerSig);
|
|
||||||
// Build the header
|
|
||||||
oneOf(headerBuilder).build();
|
|
||||||
will(returnValue(header));
|
|
||||||
}});
|
|
||||||
BundleReader r = new BundleReaderImpl(reader, size, messageParser,
|
|
||||||
headerBuilderProvider, batchBuilderProvider);
|
|
||||||
|
|
||||||
assertEquals(header, r.getHeader());
|
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testBatchBeforeHeaderThrowsException() throws Exception {
|
|
||||||
Mockery context = new Mockery();
|
|
||||||
final Reader reader = context.mock(Reader.class);
|
|
||||||
final MessageParser messageParser = context.mock(MessageParser.class);
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
final Provider<HeaderBuilder> headerBuilderProvider =
|
|
||||||
context.mock(Provider.class);
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
final Provider<BatchBuilder> batchBuilderProvider =
|
|
||||||
context.mock(Provider.class, "batchBuilderProvider");
|
|
||||||
BundleReader r = new BundleReaderImpl(reader, size, messageParser,
|
|
||||||
headerBuilderProvider, batchBuilderProvider);
|
|
||||||
|
|
||||||
try {
|
|
||||||
r.getNextBatch();
|
|
||||||
assertTrue(false);
|
|
||||||
} catch(IllegalStateException expected) {}
|
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCloseBeforeHeaderDoesNotThrowException() throws IOException,
|
|
||||||
SignatureException {
|
|
||||||
Mockery context = new Mockery();
|
|
||||||
final Reader reader = context.mock(Reader.class);
|
|
||||||
final MessageParser messageParser = context.mock(MessageParser.class);
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
final Provider<HeaderBuilder> headerBuilderProvider =
|
|
||||||
context.mock(Provider.class);
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
final Provider<BatchBuilder> batchBuilderProvider =
|
|
||||||
context.mock(Provider.class, "batchBuilderProvider");
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
oneOf(reader).close();
|
|
||||||
}});
|
|
||||||
BundleReader r = new BundleReaderImpl(reader, size, messageParser,
|
|
||||||
headerBuilderProvider, batchBuilderProvider);
|
|
||||||
|
|
||||||
r.close();
|
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testGetHeaderNoBatches() throws Exception {
|
|
||||||
Mockery context = new Mockery();
|
|
||||||
final Reader reader = context.mock(Reader.class);
|
|
||||||
final MessageParser messageParser = context.mock(MessageParser.class);
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
final Provider<HeaderBuilder> headerBuilderProvider =
|
|
||||||
context.mock(Provider.class);
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
final Provider<BatchBuilder> batchBuilderProvider =
|
|
||||||
context.mock(Provider.class, "batchBuilderProvider");
|
|
||||||
final HeaderBuilder headerBuilder = context.mock(HeaderBuilder.class);
|
|
||||||
final Header header = context.mock(Header.class);
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
oneOf(reader).setReadLimit(Header.MAX_SIZE);
|
|
||||||
oneOf(headerBuilderProvider).get();
|
|
||||||
will(returnValue(headerBuilder));
|
|
||||||
// Acks
|
|
||||||
oneOf(reader).readList(Raw.class);
|
|
||||||
will(returnValue(rawAcks));
|
|
||||||
oneOf(headerBuilder).addAcks(acks);
|
|
||||||
// Subs
|
|
||||||
oneOf(reader).readList(Raw.class);
|
|
||||||
will(returnValue(rawSubs));
|
|
||||||
oneOf(headerBuilder).addSubscriptions(subs);
|
|
||||||
// Transports
|
|
||||||
oneOf(reader).readMap(String.class, String.class);
|
|
||||||
will(returnValue(transports));
|
|
||||||
oneOf(headerBuilder).addTransports(transports);
|
|
||||||
// Signature
|
|
||||||
oneOf(reader).readRaw();
|
|
||||||
will(returnValue(headerSig));
|
|
||||||
oneOf(headerBuilder).setSignature(headerSig);
|
|
||||||
// Build the header
|
|
||||||
oneOf(headerBuilder).build();
|
|
||||||
will(returnValue(header));
|
|
||||||
// No batches
|
|
||||||
oneOf(reader).readListStart();
|
|
||||||
oneOf(reader).hasListEnd();
|
|
||||||
will(returnValue(true));
|
|
||||||
oneOf(reader).readListEnd();
|
|
||||||
// Close
|
|
||||||
oneOf(reader).close();
|
|
||||||
}});
|
|
||||||
BundleReader r = new BundleReaderImpl(reader, size, messageParser,
|
|
||||||
headerBuilderProvider, batchBuilderProvider);
|
|
||||||
|
|
||||||
assertEquals(header, r.getHeader());
|
|
||||||
assertNull(r.getNextBatch());
|
|
||||||
r.close();
|
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testGetHeaderOneBatch() throws Exception {
|
|
||||||
Mockery context = new Mockery();
|
|
||||||
final Reader reader = context.mock(Reader.class);
|
|
||||||
final MessageParser messageParser = context.mock(MessageParser.class);
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
final Provider<HeaderBuilder> headerBuilderProvider =
|
|
||||||
context.mock(Provider.class);
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
final Provider<BatchBuilder> batchBuilderProvider =
|
|
||||||
context.mock(Provider.class, "batchBuilderProvider");
|
|
||||||
final HeaderBuilder headerBuilder = context.mock(HeaderBuilder.class);
|
|
||||||
final Header header = context.mock(Header.class);
|
|
||||||
final BatchBuilder batchBuilder = context.mock(BatchBuilder.class);
|
|
||||||
final Batch batch = context.mock(Batch.class);
|
|
||||||
final Message message = context.mock(Message.class);
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
oneOf(reader).setReadLimit(Header.MAX_SIZE);
|
|
||||||
oneOf(headerBuilderProvider).get();
|
|
||||||
will(returnValue(headerBuilder));
|
|
||||||
// Acks
|
|
||||||
oneOf(reader).readList(Raw.class);
|
|
||||||
will(returnValue(rawAcks));
|
|
||||||
oneOf(headerBuilder).addAcks(acks);
|
|
||||||
// Subs
|
|
||||||
oneOf(reader).readList(Raw.class);
|
|
||||||
will(returnValue(rawSubs));
|
|
||||||
oneOf(headerBuilder).addSubscriptions(subs);
|
|
||||||
// Transports
|
|
||||||
oneOf(reader).readMap(String.class, String.class);
|
|
||||||
will(returnValue(transports));
|
|
||||||
oneOf(headerBuilder).addTransports(transports);
|
|
||||||
// Signature
|
|
||||||
oneOf(reader).readRaw();
|
|
||||||
will(returnValue(headerSig));
|
|
||||||
oneOf(headerBuilder).setSignature(headerSig);
|
|
||||||
// Build the header
|
|
||||||
oneOf(headerBuilder).build();
|
|
||||||
will(returnValue(header));
|
|
||||||
// First batch
|
|
||||||
oneOf(reader).readListStart();
|
|
||||||
oneOf(reader).hasListEnd();
|
|
||||||
will(returnValue(false));
|
|
||||||
oneOf(reader).setReadLimit(Batch.MAX_SIZE);
|
|
||||||
oneOf(batchBuilderProvider).get();
|
|
||||||
will(returnValue(batchBuilder));
|
|
||||||
oneOf(reader).readList(Raw.class);
|
|
||||||
will(returnValue(rawMessages));
|
|
||||||
oneOf(messageParser).parseMessage(messageBody);
|
|
||||||
will(returnValue(message));
|
|
||||||
oneOf(batchBuilder).addMessage(message);
|
|
||||||
oneOf(reader).readRaw();
|
|
||||||
will(returnValue(batchSig));
|
|
||||||
oneOf(batchBuilder).setSignature(batchSig);
|
|
||||||
oneOf(batchBuilder).build();
|
|
||||||
will(returnValue(batch));
|
|
||||||
// No more batches
|
|
||||||
oneOf(reader).hasListEnd();
|
|
||||||
will(returnValue(true));
|
|
||||||
oneOf(reader).readListEnd();
|
|
||||||
// Close
|
|
||||||
oneOf(reader).close();
|
|
||||||
}});
|
|
||||||
BundleReader r = new BundleReaderImpl(reader, size, messageParser,
|
|
||||||
headerBuilderProvider, batchBuilderProvider);
|
|
||||||
|
|
||||||
assertEquals(header, r.getHeader());
|
|
||||||
assertEquals(batch, r.getNextBatch());
|
|
||||||
assertNull(r.getNextBatch());
|
|
||||||
r.close();
|
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,234 +0,0 @@
|
|||||||
package net.sf.briar.protocol;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
|
||||||
import net.sf.briar.TestUtils;
|
|
||||||
import net.sf.briar.api.protocol.Batch;
|
|
||||||
import net.sf.briar.api.protocol.BatchId;
|
|
||||||
import net.sf.briar.api.protocol.BundleWriter;
|
|
||||||
import net.sf.briar.api.protocol.GroupId;
|
|
||||||
import net.sf.briar.api.protocol.Header;
|
|
||||||
import net.sf.briar.api.protocol.Message;
|
|
||||||
import net.sf.briar.api.serial.Writer;
|
|
||||||
|
|
||||||
import org.jmock.Expectations;
|
|
||||||
import org.jmock.Mockery;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
public class BundleWriterTest extends TestCase {
|
|
||||||
|
|
||||||
private final long capacity = 1024L * 1024L;
|
|
||||||
private final BatchId ack = new BatchId(TestUtils.getRandomId());
|
|
||||||
private final Set<BatchId> acks = Collections.singleton(ack);
|
|
||||||
private final GroupId sub = new GroupId(TestUtils.getRandomId());
|
|
||||||
private final Set<GroupId> subs = Collections.singleton(sub);
|
|
||||||
private final Map<String, String> transports =
|
|
||||||
Collections.singletonMap("foo", "bar");
|
|
||||||
private final byte[] headerSig = TestUtils.getRandomId();
|
|
||||||
private final byte[] messageBody = new byte[123];
|
|
||||||
private final byte[] batchSig = TestUtils.getRandomId();
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAddHeader() throws IOException {
|
|
||||||
Mockery context = new Mockery();
|
|
||||||
final Writer writer = context.mock(Writer.class);
|
|
||||||
final Header header = context.mock(Header.class);
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
// Acks
|
|
||||||
oneOf(writer).writeListStart();
|
|
||||||
oneOf(header).getAcks();
|
|
||||||
will(returnValue(acks));
|
|
||||||
oneOf(writer).writeRaw(ack);
|
|
||||||
oneOf(writer).writeListEnd();
|
|
||||||
// Subs
|
|
||||||
oneOf(writer).writeListStart();
|
|
||||||
oneOf(header).getSubscriptions();
|
|
||||||
will(returnValue(subs));
|
|
||||||
oneOf(writer).writeRaw(sub);
|
|
||||||
oneOf(writer).writeListEnd();
|
|
||||||
// Transports
|
|
||||||
oneOf(header).getTransports();
|
|
||||||
will(returnValue(transports));
|
|
||||||
oneOf(writer).writeMap(transports);
|
|
||||||
// Signature
|
|
||||||
oneOf(header).getSignature();
|
|
||||||
will(returnValue(headerSig));
|
|
||||||
oneOf(writer).writeRaw(headerSig);
|
|
||||||
}});
|
|
||||||
BundleWriter w = new BundleWriterImpl(writer, capacity);
|
|
||||||
|
|
||||||
w.addHeader(header);
|
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAddHeaderEmptyLists() throws IOException {
|
|
||||||
Mockery context = new Mockery();
|
|
||||||
final Writer writer = context.mock(Writer.class);
|
|
||||||
final Header header = context.mock(Header.class);
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
// Acks
|
|
||||||
oneOf(writer).writeListStart();
|
|
||||||
oneOf(header).getAcks();
|
|
||||||
will(returnValue(Collections.emptySet()));
|
|
||||||
oneOf(writer).writeListEnd();
|
|
||||||
// Subs
|
|
||||||
oneOf(writer).writeListStart();
|
|
||||||
oneOf(header).getSubscriptions();
|
|
||||||
will(returnValue(Collections.emptySet()));
|
|
||||||
oneOf(writer).writeListEnd();
|
|
||||||
// Transports
|
|
||||||
oneOf(header).getTransports();
|
|
||||||
will(returnValue(Collections.emptyMap()));
|
|
||||||
oneOf(writer).writeMap(Collections.emptyMap());
|
|
||||||
// Signature
|
|
||||||
oneOf(header).getSignature();
|
|
||||||
will(returnValue(headerSig));
|
|
||||||
oneOf(writer).writeRaw(headerSig);
|
|
||||||
}});
|
|
||||||
BundleWriter w = new BundleWriterImpl(writer, capacity);
|
|
||||||
|
|
||||||
w.addHeader(header);
|
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testBatchBeforeHeaderThrowsException() throws IOException {
|
|
||||||
Mockery context = new Mockery();
|
|
||||||
final Writer writer = context.mock(Writer.class);
|
|
||||||
final Batch batch = context.mock(Batch.class);
|
|
||||||
BundleWriter w = new BundleWriterImpl(writer, capacity);
|
|
||||||
|
|
||||||
try {
|
|
||||||
w.addBatch(batch);
|
|
||||||
assertTrue(false);
|
|
||||||
} catch(IllegalStateException expected) {}
|
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCloseBeforeHeaderThrowsException() throws IOException {
|
|
||||||
Mockery context = new Mockery();
|
|
||||||
final Writer writer = context.mock(Writer.class);
|
|
||||||
BundleWriter w = new BundleWriterImpl(writer, capacity);
|
|
||||||
|
|
||||||
try {
|
|
||||||
w.close();
|
|
||||||
assertTrue(false);
|
|
||||||
} catch(IllegalStateException expected) {}
|
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCloseWithoutBatchesDoesNotThrowException()
|
|
||||||
throws IOException {
|
|
||||||
Mockery context = new Mockery();
|
|
||||||
final Writer writer = context.mock(Writer.class);
|
|
||||||
final Header header = context.mock(Header.class);
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
// Acks
|
|
||||||
oneOf(writer).writeListStart();
|
|
||||||
oneOf(header).getAcks();
|
|
||||||
will(returnValue(acks));
|
|
||||||
oneOf(writer).writeRaw(ack);
|
|
||||||
oneOf(writer).writeListEnd();
|
|
||||||
// Subs
|
|
||||||
oneOf(writer).writeListStart();
|
|
||||||
oneOf(header).getSubscriptions();
|
|
||||||
will(returnValue(subs));
|
|
||||||
oneOf(writer).writeRaw(sub);
|
|
||||||
oneOf(writer).writeListEnd();
|
|
||||||
// Transports
|
|
||||||
oneOf(header).getTransports();
|
|
||||||
will(returnValue(transports));
|
|
||||||
oneOf(writer).writeMap(transports);
|
|
||||||
// Signature
|
|
||||||
oneOf(header).getSignature();
|
|
||||||
will(returnValue(headerSig));
|
|
||||||
oneOf(writer).writeRaw(headerSig);
|
|
||||||
// Close - write an empty list of batches
|
|
||||||
oneOf(writer).writeListStart();
|
|
||||||
oneOf(writer).writeListEnd();
|
|
||||||
oneOf(writer).close();
|
|
||||||
}});
|
|
||||||
BundleWriter w = new BundleWriterImpl(writer, capacity);
|
|
||||||
|
|
||||||
w.addHeader(header);
|
|
||||||
w.close();
|
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAddHeaderAndTwoBatches() throws IOException {
|
|
||||||
Mockery context = new Mockery();
|
|
||||||
final Writer writer = context.mock(Writer.class);
|
|
||||||
final Header header = context.mock(Header.class);
|
|
||||||
final Batch batch = context.mock(Batch.class);
|
|
||||||
final Message message = context.mock(Message.class);
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
// Acks
|
|
||||||
oneOf(writer).writeListStart();
|
|
||||||
oneOf(header).getAcks();
|
|
||||||
will(returnValue(acks));
|
|
||||||
oneOf(writer).writeRaw(ack);
|
|
||||||
oneOf(writer).writeListEnd();
|
|
||||||
// Subs
|
|
||||||
oneOf(writer).writeListStart();
|
|
||||||
oneOf(header).getSubscriptions();
|
|
||||||
will(returnValue(subs));
|
|
||||||
oneOf(writer).writeRaw(sub);
|
|
||||||
oneOf(writer).writeListEnd();
|
|
||||||
// Transports
|
|
||||||
oneOf(header).getTransports();
|
|
||||||
will(returnValue(transports));
|
|
||||||
oneOf(writer).writeMap(transports);
|
|
||||||
// Signature
|
|
||||||
oneOf(header).getSignature();
|
|
||||||
will(returnValue(headerSig));
|
|
||||||
oneOf(writer).writeRaw(headerSig);
|
|
||||||
// First batch
|
|
||||||
oneOf(writer).writeListStart();
|
|
||||||
oneOf(writer).writeListStart();
|
|
||||||
oneOf(batch).getMessages();
|
|
||||||
will(returnValue(Collections.singleton(message)));
|
|
||||||
oneOf(message).getBytes();
|
|
||||||
will(returnValue(messageBody));
|
|
||||||
oneOf(writer).writeRaw(messageBody);
|
|
||||||
oneOf(writer).writeListEnd();
|
|
||||||
oneOf(batch).getSignature();
|
|
||||||
will(returnValue(batchSig));
|
|
||||||
oneOf(writer).writeRaw(batchSig);
|
|
||||||
// Second batch
|
|
||||||
oneOf(writer).writeListStart();
|
|
||||||
oneOf(batch).getMessages();
|
|
||||||
will(returnValue(Collections.singleton(message)));
|
|
||||||
oneOf(message).getBytes();
|
|
||||||
will(returnValue(messageBody));
|
|
||||||
oneOf(writer).writeRaw(messageBody);
|
|
||||||
oneOf(writer).writeListEnd();
|
|
||||||
oneOf(batch).getSignature();
|
|
||||||
will(returnValue(batchSig));
|
|
||||||
oneOf(writer).writeRaw(batchSig);
|
|
||||||
// Close
|
|
||||||
oneOf(writer).writeListEnd();
|
|
||||||
oneOf(writer).close();
|
|
||||||
}});
|
|
||||||
BundleWriter w = new BundleWriterImpl(writer, capacity);
|
|
||||||
|
|
||||||
w.addHeader(header);
|
|
||||||
w.addBatch(batch);
|
|
||||||
w.addBatch(batch);
|
|
||||||
w.close();
|
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
158
test/net/sf/briar/protocol/SigningStreamTest.java
Normal file
158
test/net/sf/briar/protocol/SigningStreamTest.java
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
package net.sf.briar.protocol;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.security.DigestOutputStream;
|
||||||
|
import java.security.KeyPair;
|
||||||
|
import java.security.KeyPairGenerator;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.Signature;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class SigningStreamTest extends TestCase {
|
||||||
|
|
||||||
|
private static final String SIGNATURE_ALGO = "SHA256withRSA";
|
||||||
|
private static final String KEY_PAIR_ALGO = "RSA";
|
||||||
|
private static final String DIGEST_ALGO = "SHA-256";
|
||||||
|
|
||||||
|
private final KeyPair keyPair;
|
||||||
|
private final Signature sig;
|
||||||
|
private final MessageDigest dig;
|
||||||
|
private final Random random;
|
||||||
|
|
||||||
|
public SigningStreamTest() throws Exception {
|
||||||
|
super();
|
||||||
|
keyPair = KeyPairGenerator.getInstance(KEY_PAIR_ALGO).generateKeyPair();
|
||||||
|
sig = Signature.getInstance(SIGNATURE_ALGO);
|
||||||
|
dig = MessageDigest.getInstance(DIGEST_ALGO);
|
||||||
|
random = new Random();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOutputStreamOutputMatchesInput() throws Exception {
|
||||||
|
byte[] input = new byte[1000];
|
||||||
|
random.nextBytes(input);
|
||||||
|
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
SigningOutputStream signOut = new SigningOutputStream(out, sig);
|
||||||
|
sig.initSign(keyPair.getPrivate());
|
||||||
|
|
||||||
|
signOut.setSigning(true);
|
||||||
|
signOut.write(input, 0, 500);
|
||||||
|
signOut.setSigning(false);
|
||||||
|
signOut.write(input, 500, 250);
|
||||||
|
signOut.setSigning(true);
|
||||||
|
signOut.write(input, 750, 250);
|
||||||
|
|
||||||
|
byte[] output = out.toByteArray();
|
||||||
|
assertTrue(Arrays.equals(input, output));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInputStreamOutputMatchesInput() throws Exception {
|
||||||
|
byte[] input = new byte[1000];
|
||||||
|
random.nextBytes(input);
|
||||||
|
|
||||||
|
ByteArrayInputStream in = new ByteArrayInputStream(input);
|
||||||
|
SigningDigestingInputStream signIn =
|
||||||
|
new SigningDigestingInputStream(in, sig, dig);
|
||||||
|
sig.initVerify(keyPair.getPublic());
|
||||||
|
|
||||||
|
byte[] output = new byte[1000];
|
||||||
|
signIn.setSigning(true);
|
||||||
|
assertEquals(500, signIn.read(output, 0, 500));
|
||||||
|
signIn.setSigning(false);
|
||||||
|
assertEquals(250, signIn.read(output, 500, 250));
|
||||||
|
signIn.setSigning(true);
|
||||||
|
assertEquals(250, signIn.read(output, 750, 250));
|
||||||
|
|
||||||
|
assertTrue(Arrays.equals(input, output));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testVerificationLagsByOneByte() throws Exception {
|
||||||
|
byte[] input = new byte[1000];
|
||||||
|
random.nextBytes(input);
|
||||||
|
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
SigningOutputStream signOut = new SigningOutputStream(out, sig);
|
||||||
|
sig.initSign(keyPair.getPrivate());
|
||||||
|
|
||||||
|
// Sign bytes 0-499, skip bytes 500-749, sign bytes 750-999
|
||||||
|
signOut.setSigning(true);
|
||||||
|
signOut.write(input, 0, 500);
|
||||||
|
signOut.setSigning(false);
|
||||||
|
signOut.write(input, 500, 250);
|
||||||
|
signOut.setSigning(true);
|
||||||
|
signOut.write(input, 750, 250);
|
||||||
|
|
||||||
|
byte[] signature = sig.sign();
|
||||||
|
|
||||||
|
ByteArrayInputStream in = new ByteArrayInputStream(input);
|
||||||
|
SigningDigestingInputStream signIn =
|
||||||
|
new SigningDigestingInputStream(in, sig, dig);
|
||||||
|
sig.initVerify(keyPair.getPublic());
|
||||||
|
|
||||||
|
byte[] output = new byte[1000];
|
||||||
|
// Consume a lookahead byte
|
||||||
|
assertEquals(1, signIn.read(output, 0, 1));
|
||||||
|
// All the offsets are increased by 1 because of the lookahead byte
|
||||||
|
signIn.setSigning(true);
|
||||||
|
assertEquals(500, signIn.read(output, 1, 500));
|
||||||
|
signIn.setSigning(false);
|
||||||
|
assertEquals(250, signIn.read(output, 501, 250));
|
||||||
|
signIn.setSigning(true);
|
||||||
|
assertEquals(249, signIn.read(output, 751, 249));
|
||||||
|
// Have to reach EOF for the lookahead byte to be processed
|
||||||
|
assertEquals(-1, signIn.read());
|
||||||
|
|
||||||
|
assertTrue(Arrays.equals(input, output));
|
||||||
|
assertTrue(sig.verify(signature));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDigestionLagsByOneByte() throws Exception {
|
||||||
|
byte[] input = new byte[1000];
|
||||||
|
random.nextBytes(input);
|
||||||
|
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
DigestOutputStream digOut = new DigestOutputStream(out, dig);
|
||||||
|
dig.reset();
|
||||||
|
|
||||||
|
// Digest bytes 0-499, skip bytes 500-749, digest bytes 750-999
|
||||||
|
digOut.on(true);
|
||||||
|
digOut.write(input, 0, 500);
|
||||||
|
digOut.on(false);
|
||||||
|
digOut.write(input, 500, 250);
|
||||||
|
digOut.on(true);
|
||||||
|
digOut.write(input, 750, 250);
|
||||||
|
|
||||||
|
byte[] hash = dig.digest();
|
||||||
|
|
||||||
|
ByteArrayInputStream in = new ByteArrayInputStream(input);
|
||||||
|
SigningDigestingInputStream signIn =
|
||||||
|
new SigningDigestingInputStream(in, sig, dig);
|
||||||
|
dig.reset();
|
||||||
|
|
||||||
|
byte[] output = new byte[1000];
|
||||||
|
// Consume a lookahead byte
|
||||||
|
assertEquals(1, signIn.read(output, 0, 1));
|
||||||
|
// All the offsets are increased by 1 because of the lookahead byte
|
||||||
|
signIn.setDigesting(true);
|
||||||
|
assertEquals(500, signIn.read(output, 1, 500));
|
||||||
|
signIn.setDigesting(false);
|
||||||
|
assertEquals(250, signIn.read(output, 501, 250));
|
||||||
|
signIn.setDigesting(true);
|
||||||
|
assertEquals(249, signIn.read(output, 751, 249));
|
||||||
|
// Have to reach EOF for the lookahead byte to be processed
|
||||||
|
assertEquals(-1, signIn.read());
|
||||||
|
|
||||||
|
assertTrue(Arrays.equals(input, output));
|
||||||
|
assertTrue(Arrays.equals(hash, dig.digest()));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
package net.sf.briar.protocol;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
import net.sf.briar.api.serial.Raw;
|
|
||||||
|
|
||||||
class TestRaw implements Raw {
|
|
||||||
|
|
||||||
private final byte[] bytes;
|
|
||||||
|
|
||||||
TestRaw(byte[] bytes) {
|
|
||||||
this.bytes = bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getBytes() {
|
|
||||||
return bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return Arrays.hashCode(bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if(o instanceof Raw) return Arrays.equals(bytes, ((Raw) o).getBytes());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user