diff --git a/api/net/sf/briar/api/protocol/Batch.java b/api/net/sf/briar/api/protocol/Batch.java index 05dc321a295bc3bc004124a4fd9188951a99cbb7..e8534a72f4a08bb7a8e7bda460dabafabdc9a772 100644 --- a/api/net/sf/briar/api/protocol/Batch.java +++ b/api/net/sf/briar/api/protocol/Batch.java @@ -8,12 +8,6 @@ public interface Batch { /** Returns the batch's unique identifier. */ BatchId getId(); - /** Returns the size of the serialised batch in bytes. */ - long getSize(); - /** Returns the messages contained in the batch. */ Iterable<Message> getMessages(); - - /** Returns the sender's signature over the contents of the batch. */ - byte[] getSignature(); } \ No newline at end of file diff --git a/api/net/sf/briar/api/protocol/BatchBuilder.java b/api/net/sf/briar/api/protocol/BatchBuilder.java deleted file mode 100644 index ca7f867940d83563636cafd4e2b7c972102d2a4a..0000000000000000000000000000000000000000 --- a/api/net/sf/briar/api/protocol/BatchBuilder.java +++ /dev/null @@ -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; -} diff --git a/api/net/sf/briar/api/protocol/BundleReader.java b/api/net/sf/briar/api/protocol/BundleReader.java index b5e6390437751d2513287b148abb8e3d5f0f7721..ce32210902fb032191ccbb9c6a5752dd8991f039 100644 --- a/api/net/sf/briar/api/protocol/BundleReader.java +++ b/api/net/sf/briar/api/protocol/BundleReader.java @@ -9,9 +9,6 @@ import java.security.GeneralSecurityException; */ public interface BundleReader { - /** Returns the size of the serialised bundle in bytes. */ - long getSize() throws IOException; - /** Returns the bundle's header. */ Header getHeader() throws IOException, GeneralSecurityException; @@ -21,5 +18,5 @@ public interface BundleReader { Batch getNextBatch() throws IOException, GeneralSecurityException; /** Finishes reading the bundle. */ - void close() throws IOException; + void finish() throws IOException; } diff --git a/api/net/sf/briar/api/protocol/BundleWriter.java b/api/net/sf/briar/api/protocol/BundleWriter.java index 730a133ae6ce147ae9fd57aae7f5a6696b951408..5bb570a3ca84ac34c549990ab0cd8a1063947c0f 100644 --- a/api/net/sf/briar/api/protocol/BundleWriter.java +++ b/api/net/sf/briar/api/protocol/BundleWriter.java @@ -1,6 +1,8 @@ package net.sf.briar.api.protocol; import java.io.IOException; +import java.security.GeneralSecurityException; +import java.util.Map; /** * An interface for writing a bundle of acknowledgements, subscriptions, @@ -8,15 +10,18 @@ import java.io.IOException; */ public interface BundleWriter { - /** Returns the bundle's capacity in bytes. */ - long getCapacity() throws IOException; + /** Returns the bundle's remaining capacity in bytes. */ + long getRemainingCapacity() throws IOException; - /** Adds a header to the bundle. */ - void addHeader(Header h) throws IOException; + /** Adds a header to the bundle and returns its identifier. */ + BundleId addHeader(Iterable<BatchId> acks, Iterable<GroupId> subs, + Map<String, String> transports) throws IOException, + GeneralSecurityException; - /** Adds a batch of messages to the bundle. */ - void addBatch(Batch b) throws IOException; + /** Adds a batch to the bundle and returns its identifier. */ + BatchId addBatch(Iterable<Message> messages) throws IOException, + GeneralSecurityException; /** Finishes writing the bundle. */ - void close() throws IOException; + void finish() throws IOException; } diff --git a/api/net/sf/briar/api/protocol/Header.java b/api/net/sf/briar/api/protocol/Header.java index ce3c581a0740198eed30475c0bf47ece526ee6a0..09d762989b41abe25dc71df08127e2caa1fd3aff 100644 --- a/api/net/sf/briar/api/protocol/Header.java +++ b/api/net/sf/briar/api/protocol/Header.java @@ -11,9 +11,6 @@ public interface Header { // FIXME: Remove BundleId when refactoring is complete BundleId getId(); - /** Returns the size of the serialised header in bytes. */ - long getSize(); - /** Returns the acknowledgements contained in the header. */ Set<BatchId> getAcks(); @@ -22,7 +19,4 @@ public interface Header { /** Returns the transport details contained in the header. */ Map<String, String> getTransports(); - - /** Returns the sender's signature over the contents of the header. */ - byte[] getSignature(); } diff --git a/api/net/sf/briar/api/protocol/HeaderBuilder.java b/api/net/sf/briar/api/protocol/HeaderBuilder.java deleted file mode 100644 index c22ed3a361e2a355e7f2f75065a50d921c95f3f6..0000000000000000000000000000000000000000 --- a/api/net/sf/briar/api/protocol/HeaderBuilder.java +++ /dev/null @@ -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; -} diff --git a/api/net/sf/briar/api/protocol/MessageFactory.java b/api/net/sf/briar/api/protocol/MessageFactory.java index 33f0fdc4393d4b969e771f22a238561696eb76b5..ca3af74a996df2cb2009251cd72ec5df3f6d48de 100644 --- a/api/net/sf/briar/api/protocol/MessageFactory.java +++ b/api/net/sf/briar/api/protocol/MessageFactory.java @@ -3,5 +3,5 @@ package net.sf.briar.api.protocol; public interface MessageFactory { Message createMessage(MessageId id, MessageId parent, GroupId group, - AuthorId author, long timestamp, byte[] body); + AuthorId author, long timestamp, byte[] raw); } diff --git a/api/net/sf/briar/api/serial/Raw.java b/api/net/sf/briar/api/serial/Raw.java index 3196dcc46417bb23417de5a3dbce84ee0f638b54..5a40b2f8c5549e00436028a0996619652159fc8d 100644 --- a/api/net/sf/briar/api/serial/Raw.java +++ b/api/net/sf/briar/api/serial/Raw.java @@ -1,5 +1,9 @@ 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 { byte[] getBytes(); diff --git a/components/net/sf/briar/db/DatabaseComponentImpl.java b/components/net/sf/briar/db/DatabaseComponentImpl.java index 4fc737634d40e8abcced69301854d4d57e8663e0..45c006ecc4ffb22c884733fc70705c9dc2a00b70 100644 --- a/components/net/sf/briar/db/DatabaseComponentImpl.java +++ b/components/net/sf/briar/db/DatabaseComponentImpl.java @@ -9,13 +9,9 @@ import net.sf.briar.api.db.DatabaseComponent; import net.sf.briar.api.db.DbException; import net.sf.briar.api.db.Status; 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.MessageId; -import com.google.inject.Provider; - /** * Abstract superclass containing code shared by ReadWriteLockDatabaseComponent * and SynchronizedDatabaseComponent. @@ -28,8 +24,6 @@ DatabaseCleaner.Callback { protected final Database<Txn> db; protected final DatabaseCleaner cleaner; - protected final Provider<HeaderBuilder> headerBuilderProvider; - protected final Provider<BatchBuilder> batchBuilderProvider; private final Object spaceLock = new Object(); private final Object writeLock = new Object(); @@ -37,13 +31,9 @@ DatabaseCleaner.Callback { private long timeOfLastCheck = 0L; // Locking: spaceLock private volatile boolean writesAllowed = true; - DatabaseComponentImpl(Database<Txn> db, DatabaseCleaner cleaner, - Provider<HeaderBuilder> headerBuilderProvider, - Provider<BatchBuilder> batchBuilderProvider) { + DatabaseComponentImpl(Database<Txn> db, DatabaseCleaner cleaner) { this.db = db; this.cleaner = cleaner; - this.headerBuilderProvider = headerBuilderProvider; - this.batchBuilderProvider = batchBuilderProvider; } public void open(boolean resume) throws DbException { diff --git a/components/net/sf/briar/db/JdbcDatabase.java b/components/net/sf/briar/db/JdbcDatabase.java index 2c6c5552633b8b85e15a63a56970679780ce19eb..cabebcc17543d2a815f53106729d251fb8dd3caf 100644 --- a/components/net/sf/briar/db/JdbcDatabase.java +++ b/components/net/sf/briar/db/JdbcDatabase.java @@ -52,7 +52,7 @@ abstract class JdbcDatabase implements Database<Connection> { + " authorId XXXX NOT NULL," + " timestamp BIGINT NOT NULL," + " size INT NOT NULL," - + " body BLOB NOT NULL," + + " raw BLOB NOT NULL," + " sendability INT NOT NULL," + " PRIMARY KEY (messageId)," + " FOREIGN KEY (groupId) REFERENCES localSubscriptions (groupId)" @@ -458,7 +458,7 @@ abstract class JdbcDatabase implements Database<Connection> { try { String sql = "INSERT INTO messages" + " (messageId, parentId, groupId, authorId, timestamp, size," - + " body, sendability)" + + " raw, sendability)" + " VALUES (?, ?, ?, ?, ?, ?, ?, ?)"; ps = txn.prepareStatement(sql); ps.setBytes(1, m.getId().getBytes()); @@ -834,7 +834,7 @@ abstract class JdbcDatabase implements Database<Connection> { ResultSet rs = null; try { String sql = - "SELECT parentId, groupId, authorId, timestamp, size, body" + "SELECT parentId, groupId, authorId, timestamp, size, raw" + " FROM messages WHERE messageId = ?"; ps = txn.prepareStatement(sql); ps.setBytes(1, m.getBytes()); @@ -847,14 +847,14 @@ abstract class JdbcDatabase implements Database<Connection> { long timestamp = rs.getLong(4); int size = rs.getInt(5); Blob b = rs.getBlob(6); - byte[] body = b.getBytes(1, size); - assert body.length == size; + byte[] raw = b.getBytes(1, size); + assert raw.length == size; boolean more = rs.next(); assert !more; rs.close(); ps.close(); return messageFactory.createMessage(m, parent, group, author, - timestamp, body); + timestamp, raw); } catch(SQLException e) { tryToClose(rs); tryToClose(ps); diff --git a/components/net/sf/briar/db/ReadWriteLockDatabaseComponent.java b/components/net/sf/briar/db/ReadWriteLockDatabaseComponent.java index f9971cfc0664d98785e8edefa7b5377ccb2dfe34..5342218efc426980158a65bc6a0627835b765ba0 100644 --- a/components/net/sf/briar/db/ReadWriteLockDatabaseComponent.java +++ b/components/net/sf/briar/db/ReadWriteLockDatabaseComponent.java @@ -2,9 +2,10 @@ package net.sf.briar.db; import java.io.IOException; import java.security.GeneralSecurityException; -import java.security.SignatureException; +import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.Set; 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.protocol.AuthorId; 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.BundleId; import net.sf.briar.api.protocol.BundleReader; 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.HeaderBuilder; import net.sf.briar.api.protocol.Message; import net.sf.briar.api.protocol.MessageId; import com.google.inject.Inject; -import com.google.inject.Provider; /** * An implementation of DatabaseComponent using reentrant read-write locks. @@ -59,10 +57,8 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { new ReentrantReadWriteLock(true); @Inject - ReadWriteLockDatabaseComponent(Database<Txn> db, DatabaseCleaner cleaner, - Provider<HeaderBuilder> headerBuilderProvider, - Provider<BatchBuilder> batchBuilderProvider) { - super(db, cleaner, headerBuilderProvider, batchBuilderProvider); + ReadWriteLockDatabaseComponent(Database<Txn> db, DatabaseCleaner cleaner) { + super(db, cleaner); } public void close() throws DbException { @@ -195,18 +191,18 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { public void generateBundle(ContactId c, BundleWriter b) throws DbException, IOException, GeneralSecurityException { 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 contactLock.readLock().lock(); try { if(!containsContact(c)) throw new NoSuchContactException(); - h = headerBuilderProvider.get(); messageStatusLock.writeLock().lock(); try { Txn txn = db.startTransaction(); try { - Set<BatchId> acks = db.removeBatchesToAck(txn, c); - h.addAcks(acks); + acks = db.removeBatchesToAck(txn, c); if(LOG.isLoggable(Level.FINE)) LOG.fine("Added " + acks.size() + " acks"); db.commitTransaction(txn); @@ -228,8 +224,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { try { Txn txn = db.startTransaction(); try { - Set<GroupId> subs = db.getSubscriptions(txn); - h.addSubscriptions(subs); + subs = db.getSubscriptions(txn); if(LOG.isLoggable(Level.FINE)) LOG.fine("Added " + subs.size() + " subscriptions"); db.commitTransaction(txn); @@ -251,8 +246,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { try { Txn txn = db.startTransaction(); try { - Map<String, String> transports = db.getTransports(txn); - h.addTransports(transports); + transports = db.getTransports(txn); if(LOG.isLoggable(Level.FINE)) LOG.fine("Added " + transports.size() + " transports"); db.commitTransaction(txn); @@ -266,28 +260,16 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { } finally { contactLock.readLock().unlock(); } - // Sign the header and add it to the bundle - Header header = h.build(); - long capacity = b.getCapacity(); - capacity -= header.getSize(); - b.addHeader(header); + // Add the header to the bundle + b.addHeader(acks, subs, transports); // Add as many messages as possible to the bundle - while(true) { - Batch batch = fillBatch(c, capacity); - 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(); + while(fillBatch(c, b)); + b.finish(); if(LOG.isLoggable(Level.FINE)) LOG.fine("Bundle generated"); System.gc(); } - private Batch fillBatch(ContactId c, long capacity) throws DbException, + private boolean fillBatch(ContactId c, BundleWriter b) throws DbException, IOException, GeneralSecurityException { contactLock.readLock().lock(); try { @@ -295,31 +277,38 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { messageLock.readLock().lock(); try { Set<MessageId> sent; - Batch batch; + int bytesSent = 0; + BatchId batchId; messageStatusLock.readLock().lock(); try { Txn txn = db.startTransaction(); try { - capacity = Math.min(capacity, Batch.MAX_SIZE); + long capacity = Math.min(b.getRemainingCapacity(), + Batch.MAX_SIZE); Iterator<MessageId> it = db.getSendableMessages(txn, c, capacity).iterator(); if(!it.hasNext()) { db.commitTransaction(txn); - return null; // No more messages to send + return false; // No more messages to send } sent = new HashSet<MessageId>(); - BatchBuilder b = batchBuilderProvider.get(); + List<Message> messages = new ArrayList<Message>(); while(it.hasNext()) { 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); } - batch = b.build(); + batchId = b.addBatch(messages); db.commitTransaction(txn); } catch(DbException e) { db.abortTransaction(txn); throw e; - } catch(SignatureException e) { + } catch(IOException e) { + db.abortTransaction(txn); + throw e; + } catch(GeneralSecurityException e) { db.abortTransaction(txn); throw e; } @@ -332,9 +321,10 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { Txn txn = db.startTransaction(); try { assert !sent.isEmpty(); - db.addOutstandingBatch(txn, c, batch.getId(), sent); + db.addOutstandingBatch(txn, c, batchId, sent); 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) { db.abortTransaction(txn); throw e; @@ -443,9 +433,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { public void receiveBundle(ContactId c, BundleReader b) throws DbException, IOException, GeneralSecurityException { - if(LOG.isLoggable(Level.FINE)) - LOG.fine("Received bundle from " + c + ", " - + b.getSize() + " bytes"); + if(LOG.isLoggable(Level.FINE)) LOG.fine("Received bundle from " + c); Header h; // Mark all messages in acked batches as seen contactLock.readLock().lock(); @@ -536,7 +524,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { } if(LOG.isLoggable(Level.FINE)) LOG.fine("Received " + batches + " batches"); - b.close(); + b.finish(); retransmitLostBatches(c, h.getId()); System.gc(); } diff --git a/components/net/sf/briar/db/SynchronizedDatabaseComponent.java b/components/net/sf/briar/db/SynchronizedDatabaseComponent.java index f7caf0910741e1ed6cb11e7958dd330e64535ac6..9916738ea62fe2d99ed9028ae2cf05a760b70586 100644 --- a/components/net/sf/briar/db/SynchronizedDatabaseComponent.java +++ b/components/net/sf/briar/db/SynchronizedDatabaseComponent.java @@ -3,8 +3,10 @@ package net.sf.briar.db; import java.io.IOException; import java.security.GeneralSecurityException; import java.security.SignatureException; +import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.Set; 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.protocol.AuthorId; 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.BundleId; import net.sf.briar.api.protocol.BundleReader; 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.HeaderBuilder; import net.sf.briar.api.protocol.Message; import net.sf.briar.api.protocol.MessageId; import com.google.inject.Inject; -import com.google.inject.Provider; /** * An implementation of DatabaseComponent using Java synchronization. This @@ -52,10 +51,8 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { private final Object transportLock = new Object(); @Inject - SynchronizedDatabaseComponent(Database<Txn> db, DatabaseCleaner cleaner, - Provider<HeaderBuilder> headerBuilderProvider, - Provider<BatchBuilder> batchBuilderProvider) { - super(db, cleaner, headerBuilderProvider, batchBuilderProvider); + SynchronizedDatabaseComponent(Database<Txn> db, DatabaseCleaner cleaner) { + super(db, cleaner); } public void close() throws DbException { @@ -148,16 +145,16 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { public void generateBundle(ContactId c, BundleWriter b) throws DbException, IOException, GeneralSecurityException { 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 synchronized(contactLock) { if(!containsContact(c)) throw new NoSuchContactException(); - h = headerBuilderProvider.get(); synchronized(messageStatusLock) { Txn txn = db.startTransaction(); try { - Set<BatchId> acks = db.removeBatchesToAck(txn, c); - h.addAcks(acks); + acks = db.removeBatchesToAck(txn, c); if(LOG.isLoggable(Level.FINE)) LOG.fine("Added " + acks.size() + " acks"); db.commitTransaction(txn); @@ -173,8 +170,7 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { synchronized(subscriptionLock) { Txn txn = db.startTransaction(); try { - Set<GroupId> subs = db.getSubscriptions(txn); - h.addSubscriptions(subs); + subs = db.getSubscriptions(txn); if(LOG.isLoggable(Level.FINE)) LOG.fine("Added " + subs.size() + " subscriptions"); db.commitTransaction(txn); @@ -190,8 +186,7 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { synchronized(transportLock) { Txn txn = db.startTransaction(); try { - Map<String, String> transports = db.getTransports(txn); - h.addTransports(transports); + transports = db.getTransports(txn); if(LOG.isLoggable(Level.FINE)) LOG.fine("Added " + transports.size() + " transports"); db.commitTransaction(txn); @@ -201,28 +196,16 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { } } } - // Sign the header and add it to the bundle - Header header = h.build(); - long capacity = b.getCapacity(); - capacity -= header.getSize(); - b.addHeader(header); + // Add the header to the bundle + b.addHeader(acks, subs, transports); // Add as many messages as possible to the bundle - while(true) { - Batch batch = fillBatch(c, capacity); - 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(); + while(fillBatch(c, b)); + b.finish(); if(LOG.isLoggable(Level.FINE)) LOG.fine("Bundle generated"); System.gc(); } - private Batch fillBatch(ContactId c, long capacity) throws DbException, + private boolean fillBatch(ContactId c, BundleWriter b) throws DbException, IOException, GeneralSecurityException { synchronized(contactLock) { if(!containsContact(c)) throw new NoSuchContactException(); @@ -230,26 +213,31 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { synchronized(messageStatusLock) { Txn txn = db.startTransaction(); try { - capacity = Math.min(capacity, Batch.MAX_SIZE); + long capacity = Math.min(b.getRemainingCapacity(), + Batch.MAX_SIZE); Iterator<MessageId> it = db.getSendableMessages(txn, c, capacity).iterator(); if(!it.hasNext()) { 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>(); + List<Message> messages = new ArrayList<Message>(); + int bytesSent = 0; while(it.hasNext()) { 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); } - Batch batch = b.build(); + BatchId batchId = b.addBatch(messages); // Record the contents of the batch assert !sent.isEmpty(); - db.addOutstandingBatch(txn, c, batch.getId(), sent); + db.addOutstandingBatch(txn, c, batchId, sent); 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) { db.abortTransaction(txn); throw e; @@ -337,9 +325,7 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { public void receiveBundle(ContactId c, BundleReader b) throws DbException, IOException, GeneralSecurityException { - if(LOG.isLoggable(Level.FINE)) - LOG.fine("Received bundle from " + c + ", " - + b.getSize() + " bytes"); + if(LOG.isLoggable(Level.FINE)) LOG.fine("Received bundle from " + c); Header h; // Mark all messages in acked batches as seen synchronized(contactLock) { @@ -409,7 +395,7 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { } if(LOG.isLoggable(Level.FINE)) LOG.fine("Received " + batches + " batches"); - b.close(); + b.finish(); retransmitLostBatches(c, h.getId()); System.gc(); } diff --git a/components/net/sf/briar/protocol/BatchBuilderImpl.java b/components/net/sf/briar/protocol/BatchBuilderImpl.java deleted file mode 100644 index 824ac38c56708f4bd082a5356a88695c7f331a15..0000000000000000000000000000000000000000 --- a/components/net/sf/briar/protocol/BatchBuilderImpl.java +++ /dev/null @@ -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); - } -} diff --git a/components/net/sf/briar/protocol/BatchFactory.java b/components/net/sf/briar/protocol/BatchFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..8efe7fc0693f6a2dd96b361bbc868d36dd72cff0 --- /dev/null +++ b/components/net/sf/briar/protocol/BatchFactory.java @@ -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); +} diff --git a/components/net/sf/briar/protocol/BatchFactoryImpl.java b/components/net/sf/briar/protocol/BatchFactoryImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..5254190d760984391c217a49a0c252bc7335c4d7 --- /dev/null +++ b/components/net/sf/briar/protocol/BatchFactoryImpl.java @@ -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); + } +} diff --git a/components/net/sf/briar/protocol/BatchImpl.java b/components/net/sf/briar/protocol/BatchImpl.java index 97ccd5a71c8c0d70ffd3dbbd310cecddbf8cb86f..9f7244ff00432fcb78358068e6dae801a63f6f36 100644 --- a/components/net/sf/briar/protocol/BatchImpl.java +++ b/components/net/sf/briar/protocol/BatchImpl.java @@ -10,30 +10,18 @@ import net.sf.briar.api.protocol.Message; class BatchImpl implements Batch { private final BatchId id; - private final long size; 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.size = size; this.messages = messages; - this.signature = signature; } public BatchId getId() { return id; } - public long getSize() { - return size; - } - public Iterable<Message> getMessages() { return messages; } - - public byte[] getSignature() { - return signature; - } } diff --git a/components/net/sf/briar/protocol/BundleReaderImpl.java b/components/net/sf/briar/protocol/BundleReaderImpl.java index ea397abdf279b2275e62f7b0d5d61f99c827367e..6461071db1ae3ce985f2573fd3e532c7c6407138 100644 --- a/components/net/sf/briar/protocol/BundleReaderImpl.java +++ b/components/net/sf/briar/protocol/BundleReaderImpl.java @@ -1,56 +1,70 @@ package net.sf.briar.protocol; import java.io.IOException; +import java.io.InputStream; 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.List; import java.util.Map; import java.util.Set; 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.BundleId; 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.protocol.UniqueId; import net.sf.briar.api.serial.FormatException; import net.sf.briar.api.serial.Raw; import net.sf.briar.api.serial.Reader; - -import com.google.inject.Provider; +import net.sf.briar.api.serial.ReaderFactory; /** A bundle that deserialises its contents on demand using a reader. */ class BundleReaderImpl implements BundleReader { private static enum State { START, FIRST_BATCH, MORE_BATCHES, END }; + private final SigningDigestingInputStream in; 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 Provider<HeaderBuilder> headerBuilderProvider; - private final Provider<BatchBuilder> batchBuilderProvider; + private final HeaderFactory headerFactory; + private final BatchFactory batchFactory; private State state = State.START; - BundleReaderImpl(Reader r, long size, MessageParser messageParser, - Provider<HeaderBuilder> headerBuilderProvider, - Provider<BatchBuilder> batchBuilderProvider) { - this.r = r; - this.size = size; + BundleReaderImpl(InputStream in, ReaderFactory readerFactory, + PublicKey publicKey, Signature signature, + MessageDigest messageDigest, MessageParser messageParser, + HeaderFactory headerFactory, BatchFactory batchFactory) { + 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.headerBuilderProvider = headerBuilderProvider; - this.batchBuilderProvider = batchBuilderProvider; - } - - public long getSize() { - return size; + this.headerFactory = headerFactory; + this.batchFactory = batchFactory; } public Header getHeader() throws IOException, GeneralSecurityException { 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); Set<BatchId> acks = new HashSet<BatchId>(); for(Raw raw : r.readList(Raw.class)) { @@ -64,15 +78,16 @@ class BundleReaderImpl implements BundleReader { if(b.length != UniqueId.LENGTH) throw new FormatException(); 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(); - state = State.FIRST_BATCH; - HeaderBuilder h = headerBuilderProvider.get(); - h.addAcks(acks); - h.addSubscriptions(subs); - h.addTransports(transports); - h.setSignature(sig); - return h.build(); + in.setDigesting(false); + if(!signature.verify(sig)) throw new SignatureException(); + // Build and return the header + BundleId id = new BundleId(messageDigest.digest()); + return headerFactory.createHeader(id, acks, subs, transports); } public Batch getNextBatch() throws IOException, GeneralSecurityException { @@ -86,19 +101,31 @@ class BundleReaderImpl implements BundleReader { state = State.END; 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); - List<Raw> messages = r.readList(Raw.class); - BatchBuilder b = batchBuilderProvider.get(); - for(Raw r : messages) { + List<Raw> rawMessages = r.readList(Raw.class); + in.setSigning(false); + // Read and verify the signature + byte[] sig = r.readRaw(); + in.setDigesting(false); + 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()); - b.addMessage(m); + messages.add(m); } - byte[] sig = r.readRaw(); - b.setSignature(sig); - return b.build(); + // 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(); } } diff --git a/components/net/sf/briar/protocol/BundleWriterImpl.java b/components/net/sf/briar/protocol/BundleWriterImpl.java index ba3f3f830691dc06e9ccb0ef0f7a9aa1b3ec2630..6dcdb65a15a2135e3d561441036f36faa723a956 100644 --- a/components/net/sf/briar/protocol/BundleWriterImpl.java +++ b/components/net/sf/briar/protocol/BundleWriterImpl.java @@ -1,59 +1,100 @@ package net.sf.briar.protocol; 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.BundleId; 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 net.sf.briar.api.serial.WriterFactory; /** A bundle builder that serialises its contents using a writer. */ class BundleWriterImpl implements BundleWriter { private static enum State { START, FIRST_BATCH, MORE_BATCHES, END }; + private final SigningOutputStream out; private final Writer w; + private final PrivateKey privateKey; + private final Signature signature; + private final MessageDigest messageDigest; private final long capacity; private State state = State.START; - BundleWriterImpl(Writer w, long capacity) { - this.w = w; + BundleWriterImpl(OutputStream out, WriterFactory writerFactory, + 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; } - public long getCapacity() { - return capacity; + public long getRemainingCapacity() { + 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(); + // Initialise the output stream + signature.initSign(privateKey); + messageDigest.reset(); + // Write the data to be signed + out.setSigning(true); w.writeListStart(); - for(BatchId ack : h.getAcks()) w.writeRaw(ack); + for(BatchId ack : acks) w.writeRaw(ack); w.writeListEnd(); w.writeListStart(); - for(GroupId sub : h.getSubscriptions()) w.writeRaw(sub); + for(GroupId sub : subs) w.writeRaw(sub); w.writeListEnd(); - w.writeMap(h.getTransports()); - w.writeRaw(h.getSignature()); + w.writeMap(transports); + out.setSigning(false); + // Create and write the signature + byte[] sig = signature.sign(); + w.writeRaw(sig); + // Calculate and return the ID 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) { w.writeListStart(); state = State.MORE_BATCHES; } 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(); - for(Message m : b.getMessages()) w.writeRaw(m.getBytes()); + for(Message m : messages) w.writeRaw(m); 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) { w.writeListStart(); state = State.MORE_BATCHES; diff --git a/components/net/sf/briar/protocol/HeaderBuilderImpl.java b/components/net/sf/briar/protocol/HeaderBuilderImpl.java deleted file mode 100644 index d40debd72c46457c59bddf9b5c4ecb2057248023..0000000000000000000000000000000000000000 --- a/components/net/sf/briar/protocol/HeaderBuilderImpl.java +++ /dev/null @@ -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)); - } - } -} diff --git a/components/net/sf/briar/protocol/HeaderFactory.java b/components/net/sf/briar/protocol/HeaderFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..06c47eac93c26ff79268837ce4fe483be9e731f4 --- /dev/null +++ b/components/net/sf/briar/protocol/HeaderFactory.java @@ -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); +} diff --git a/components/net/sf/briar/protocol/HeaderFactoryImpl.java b/components/net/sf/briar/protocol/HeaderFactoryImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..ca71a7b02cae351e9a37bf14f36be087e90268d9 --- /dev/null +++ b/components/net/sf/briar/protocol/HeaderFactoryImpl.java @@ -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); + } +} diff --git a/components/net/sf/briar/protocol/HeaderImpl.java b/components/net/sf/briar/protocol/HeaderImpl.java index 763daf0f17e43f24bc4b2a0ec4b88edca2ba4efe..c1f81c1c126884771667fc1f9df2b305f678bb9f 100644 --- a/components/net/sf/briar/protocol/HeaderImpl.java +++ b/components/net/sf/briar/protocol/HeaderImpl.java @@ -12,44 +12,31 @@ import net.sf.briar.api.protocol.Header; class HeaderImpl implements Header { private final BundleId id; - private final long size; private final Set<BatchId> acks; - private final Set<GroupId> subscriptions; + private final Set<GroupId> subs; private final Map<String, String> transports; - private final byte[] signature; - HeaderImpl(BundleId id, long size, Set<BatchId> acks, - Set<GroupId> subscriptions, Map<String, String> transports, - byte[] signature) { + HeaderImpl(BundleId id, Set<BatchId> acks, Set<GroupId> subs, + Map<String, String> transports) { this.id = id; - this.size = size; this.acks = acks; - this.subscriptions = subscriptions; + this.subs = subs; this.transports = transports; - this.signature = signature; } public BundleId getId() { return id; } - public long getSize() { - return size; - } - public Set<BatchId> getAcks() { return acks; } public Set<GroupId> getSubscriptions() { - return subscriptions; + return subs; } public Map<String, String> getTransports() { return transports; } - - public byte[] getSignature() { - return signature; - } } diff --git a/components/net/sf/briar/protocol/IncomingBatchBuilder.java b/components/net/sf/briar/protocol/IncomingBatchBuilder.java deleted file mode 100644 index 30f5952fac422e2ef2b39570957b660133555de2..0000000000000000000000000000000000000000 --- a/components/net/sf/briar/protocol/IncomingBatchBuilder.java +++ /dev/null @@ -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); - } -} diff --git a/components/net/sf/briar/protocol/IncomingHeaderBuilder.java b/components/net/sf/briar/protocol/IncomingHeaderBuilder.java deleted file mode 100644 index e83b07b8cfb6b1d57f52e2c10cf723cd56a5b14d..0000000000000000000000000000000000000000 --- a/components/net/sf/briar/protocol/IncomingHeaderBuilder.java +++ /dev/null @@ -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); - } -} diff --git a/components/net/sf/briar/protocol/OutgoingBatchBuilder.java b/components/net/sf/briar/protocol/OutgoingBatchBuilder.java deleted file mode 100644 index 118886098db5e273786f1cac7944260471d6d3ed..0000000000000000000000000000000000000000 --- a/components/net/sf/briar/protocol/OutgoingBatchBuilder.java +++ /dev/null @@ -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); - } -} diff --git a/components/net/sf/briar/protocol/OutgoingHeaderBuilder.java b/components/net/sf/briar/protocol/OutgoingHeaderBuilder.java deleted file mode 100644 index 970624defcde4069d2e26771a1b086dc103f64b0..0000000000000000000000000000000000000000 --- a/components/net/sf/briar/protocol/OutgoingHeaderBuilder.java +++ /dev/null @@ -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); - } -} diff --git a/components/net/sf/briar/protocol/SigningDigestingInputStream.java b/components/net/sf/briar/protocol/SigningDigestingInputStream.java new file mode 100644 index 0000000000000000000000000000000000000000..d95768eff1125b1218f7b780e28925bbd978387c --- /dev/null +++ b/components/net/sf/briar/protocol/SigningDigestingInputStream.java @@ -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(); + } +} diff --git a/components/net/sf/briar/protocol/SigningOutputStream.java b/components/net/sf/briar/protocol/SigningOutputStream.java new file mode 100644 index 0000000000000000000000000000000000000000..a00df05c6fc16e498f57569739ab158c3b812cdd --- /dev/null +++ b/components/net/sf/briar/protocol/SigningOutputStream.java @@ -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); + } + } + } +} diff --git a/test/net/sf/briar/db/DatabaseComponentImplTest.java b/test/net/sf/briar/db/DatabaseComponentImplTest.java index ba3e7a4e08fc72c9ba66e652f2dde963f0cb51f8..98e39c1bc67da8cea668620f08a11ebcfde2a2af 100644 --- a/test/net/sf/briar/db/DatabaseComponentImplTest.java +++ b/test/net/sf/briar/db/DatabaseComponentImplTest.java @@ -6,8 +6,6 @@ import static net.sf.briar.api.db.DatabaseComponent.MIN_FREE_SPACE; import java.util.Collections; 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.db.DatabaseCleaner.Callback; @@ -15,8 +13,6 @@ import org.jmock.Expectations; import org.jmock.Mockery; import org.junit.Test; -import com.google.inject.Provider; - /** * Tests that use the DatabaseCleaner.Callback interface of * DatabaseComponentImpl. @@ -24,9 +20,7 @@ import com.google.inject.Provider; public abstract class DatabaseComponentImplTest extends DatabaseComponentTest { protected abstract <T> DatabaseComponentImpl<T> createDatabaseComponentImpl( - Database<T> database, DatabaseCleaner cleaner, - Provider<HeaderBuilder> headerBuilderProvider, - Provider<BatchBuilder> batchBuilderProvider); + Database<T> database, DatabaseCleaner cleaner); @Test public void testNotCleanedIfEnoughFreeSpace() throws DbException { @@ -34,18 +28,11 @@ public abstract class DatabaseComponentImplTest extends DatabaseComponentTest { @SuppressWarnings("unchecked") final Database<Object> database = context.mock(Database.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() {{ oneOf(database).getFreeSpace(); will(returnValue(MIN_FREE_SPACE)); }}); - Callback db = createDatabaseComponentImpl(database, cleaner, - headerBuilderProvider, batchBuilderProvider); + Callback db = createDatabaseComponentImpl(database, cleaner); db.checkFreeSpaceAndClean(); @@ -58,12 +45,6 @@ public abstract class DatabaseComponentImplTest extends DatabaseComponentTest { @SuppressWarnings("unchecked") final Database<Object> database = context.mock(Database.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() {{ oneOf(database).getFreeSpace(); will(returnValue(MIN_FREE_SPACE - 1)); @@ -76,8 +57,7 @@ public abstract class DatabaseComponentImplTest extends DatabaseComponentTest { oneOf(database).getFreeSpace(); will(returnValue(MIN_FREE_SPACE)); }}); - Callback db = createDatabaseComponentImpl(database, cleaner, - headerBuilderProvider, batchBuilderProvider); + Callback db = createDatabaseComponentImpl(database, cleaner); db.checkFreeSpaceAndClean(); @@ -91,12 +71,6 @@ public abstract class DatabaseComponentImplTest extends DatabaseComponentTest { @SuppressWarnings("unchecked") final Database<Object> database = context.mock(Database.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() {{ oneOf(database).getFreeSpace(); will(returnValue(MIN_FREE_SPACE - 1)); @@ -111,8 +85,7 @@ public abstract class DatabaseComponentImplTest extends DatabaseComponentTest { oneOf(database).getFreeSpace(); will(returnValue(MIN_FREE_SPACE)); }}); - Callback db = createDatabaseComponentImpl(database, cleaner, - headerBuilderProvider, batchBuilderProvider); + Callback db = createDatabaseComponentImpl(database, cleaner); db.checkFreeSpaceAndClean(); @@ -126,12 +99,6 @@ public abstract class DatabaseComponentImplTest extends DatabaseComponentTest { @SuppressWarnings("unchecked") final Database<Object> database = context.mock(Database.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() {{ oneOf(database).getFreeSpace(); will(returnValue(MIN_FREE_SPACE - 1)); @@ -148,8 +115,7 @@ public abstract class DatabaseComponentImplTest extends DatabaseComponentTest { oneOf(database).getFreeSpace(); will(returnValue(MIN_FREE_SPACE)); }}); - Callback db = createDatabaseComponentImpl(database, cleaner, - headerBuilderProvider, batchBuilderProvider); + Callback db = createDatabaseComponentImpl(database, cleaner); db.checkFreeSpaceAndClean(); diff --git a/test/net/sf/briar/db/DatabaseComponentTest.java b/test/net/sf/briar/db/DatabaseComponentTest.java index fc8fa863432d4351d4cc9d80d79f2604d187b488..ebb90483ca4706f98e429d5598d8220c832b7781 100644 --- a/test/net/sf/briar/db/DatabaseComponentTest.java +++ b/test/net/sf/briar/db/DatabaseComponentTest.java @@ -14,14 +14,12 @@ import net.sf.briar.api.db.NoSuchContactException; import net.sf.briar.api.db.Status; import net.sf.briar.api.protocol.AuthorId; 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.BundleId; import net.sf.briar.api.protocol.BundleReader; 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.HeaderBuilder; import net.sf.briar.api.protocol.Message; import net.sf.briar.api.protocol.MessageId; import net.sf.briar.protocol.MessageImpl; @@ -30,8 +28,6 @@ import org.jmock.Expectations; import org.jmock.Mockery; import org.junit.Test; -import com.google.inject.Provider; - public abstract class DatabaseComponentTest extends TestCase { protected final Object txn = new Object(); @@ -73,9 +69,7 @@ public abstract class DatabaseComponentTest extends TestCase { } protected abstract <T> DatabaseComponent createDatabaseComponent( - Database<T> database, DatabaseCleaner cleaner, - Provider<HeaderBuilder> headerBuilderProvider, - Provider<BatchBuilder> batchBuilderProvider); + Database<T> database, DatabaseCleaner cleaner); @Test public void testSimpleCalls() throws DbException { @@ -85,12 +79,6 @@ public abstract class DatabaseComponentTest extends TestCase { @SuppressWarnings("unchecked") final Database<Object> database = context.mock(Database.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() {{ allowing(database).startTransaction(); will(returnValue(txn)); @@ -129,8 +117,7 @@ public abstract class DatabaseComponentTest extends TestCase { oneOf(cleaner).stopCleaning(); oneOf(database).close(); }}); - DatabaseComponent db = createDatabaseComponent(database, cleaner, - headerBuilderProvider, batchBuilderProvider); + DatabaseComponent db = createDatabaseComponent(database, cleaner); db.open(false); assertEquals(Rating.UNRATED, db.getRating(authorId)); @@ -153,12 +140,6 @@ public abstract class DatabaseComponentTest extends TestCase { @SuppressWarnings("unchecked") final Database<Object> database = context.mock(Database.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() {{ // setRating(Rating.GOOD) allowing(database).startTransaction(); @@ -175,8 +156,7 @@ public abstract class DatabaseComponentTest extends TestCase { will(returnValue(MessageId.NONE)); oneOf(database).commitTransaction(txn); }}); - DatabaseComponent db = createDatabaseComponent(database, cleaner, - headerBuilderProvider, batchBuilderProvider); + DatabaseComponent db = createDatabaseComponent(database, cleaner); db.setRating(authorId, Rating.GOOD); @@ -189,12 +169,6 @@ public abstract class DatabaseComponentTest extends TestCase { @SuppressWarnings("unchecked") final Database<Object> database = context.mock(Database.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() {{ // setRating(Rating.GOOD) oneOf(database).startTransaction(); @@ -214,8 +188,7 @@ public abstract class DatabaseComponentTest extends TestCase { will(returnValue(false)); oneOf(database).commitTransaction(txn); }}); - DatabaseComponent db = createDatabaseComponent(database, cleaner, - headerBuilderProvider, batchBuilderProvider); + DatabaseComponent db = createDatabaseComponent(database, cleaner); db.setRating(authorId, Rating.GOOD); @@ -229,12 +202,6 @@ public abstract class DatabaseComponentTest extends TestCase { @SuppressWarnings("unchecked") final Database<Object> database = context.mock(Database.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() {{ // setRating(Rating.GOOD) oneOf(database).startTransaction(); @@ -258,8 +225,7 @@ public abstract class DatabaseComponentTest extends TestCase { will(returnValue(groupId1)); oneOf(database).commitTransaction(txn); }}); - DatabaseComponent db = createDatabaseComponent(database, cleaner, - headerBuilderProvider, batchBuilderProvider); + DatabaseComponent db = createDatabaseComponent(database, cleaner); db.setRating(authorId, Rating.GOOD); @@ -273,12 +239,6 @@ public abstract class DatabaseComponentTest extends TestCase { @SuppressWarnings("unchecked") final Database<Object> database = context.mock(Database.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() {{ // setRating(Rating.GOOD) oneOf(database).startTransaction(); @@ -305,8 +265,7 @@ public abstract class DatabaseComponentTest extends TestCase { oneOf(database).setSendability(txn, parentId, 2); oneOf(database).commitTransaction(txn); }}); - DatabaseComponent db = createDatabaseComponent(database, cleaner, - headerBuilderProvider, batchBuilderProvider); + DatabaseComponent db = createDatabaseComponent(database, cleaner); db.setRating(authorId, Rating.GOOD); @@ -320,12 +279,6 @@ public abstract class DatabaseComponentTest extends TestCase { @SuppressWarnings("unchecked") final Database<Object> database = context.mock(Database.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() {{ // setRating(Rating.GOOD) oneOf(database).startTransaction(); @@ -354,8 +307,7 @@ public abstract class DatabaseComponentTest extends TestCase { will(returnValue(MessageId.NONE)); oneOf(database).commitTransaction(txn); }}); - DatabaseComponent db = createDatabaseComponent(database, cleaner, - headerBuilderProvider, batchBuilderProvider); + DatabaseComponent db = createDatabaseComponent(database, cleaner); db.setRating(authorId, Rating.GOOD); @@ -369,12 +321,6 @@ public abstract class DatabaseComponentTest extends TestCase { @SuppressWarnings("unchecked") final Database<Object> database = context.mock(Database.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() {{ // addLocallyGeneratedMessage(message) oneOf(database).startTransaction(); @@ -383,8 +329,7 @@ public abstract class DatabaseComponentTest extends TestCase { will(returnValue(false)); oneOf(database).commitTransaction(txn); }}); - DatabaseComponent db = createDatabaseComponent(database, cleaner, - headerBuilderProvider, batchBuilderProvider); + DatabaseComponent db = createDatabaseComponent(database, cleaner); db.addLocallyGeneratedMessage(message); @@ -397,12 +342,6 @@ public abstract class DatabaseComponentTest extends TestCase { @SuppressWarnings("unchecked") final Database<Object> database = context.mock(Database.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() {{ // addLocallyGeneratedMessage(message) oneOf(database).startTransaction(); @@ -413,8 +352,7 @@ public abstract class DatabaseComponentTest extends TestCase { will(returnValue(false)); oneOf(database).commitTransaction(txn); }}); - DatabaseComponent db = createDatabaseComponent(database, cleaner, - headerBuilderProvider, batchBuilderProvider); + DatabaseComponent db = createDatabaseComponent(database, cleaner); db.addLocallyGeneratedMessage(message); @@ -427,12 +365,6 @@ public abstract class DatabaseComponentTest extends TestCase { @SuppressWarnings("unchecked") final Database<Object> database = context.mock(Database.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() {{ // addLocallyGeneratedMessage(message) oneOf(database).startTransaction(); @@ -452,8 +384,7 @@ public abstract class DatabaseComponentTest extends TestCase { oneOf(database).setSendability(txn, messageId, 0); oneOf(database).commitTransaction(txn); }}); - DatabaseComponent db = createDatabaseComponent(database, cleaner, - headerBuilderProvider, batchBuilderProvider); + DatabaseComponent db = createDatabaseComponent(database, cleaner); db.addLocallyGeneratedMessage(message); @@ -467,12 +398,6 @@ public abstract class DatabaseComponentTest extends TestCase { @SuppressWarnings("unchecked") final Database<Object> database = context.mock(Database.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() {{ // addLocallyGeneratedMessage(message) oneOf(database).startTransaction(); @@ -495,8 +420,7 @@ public abstract class DatabaseComponentTest extends TestCase { will(returnValue(MessageId.NONE)); oneOf(database).commitTransaction(txn); }}); - DatabaseComponent db = createDatabaseComponent(database, cleaner, - headerBuilderProvider, batchBuilderProvider); + DatabaseComponent db = createDatabaseComponent(database, cleaner); db.addLocallyGeneratedMessage(message); @@ -510,12 +434,6 @@ public abstract class DatabaseComponentTest extends TestCase { @SuppressWarnings("unchecked") final Database<Object> database = context.mock(Database.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); context.checking(new Expectations() {{ // Check that the contact is still in the DB @@ -525,8 +443,7 @@ public abstract class DatabaseComponentTest extends TestCase { will(returnValue(false)); oneOf(database).commitTransaction(txn); }}); - DatabaseComponent db = createDatabaseComponent(database, cleaner, - headerBuilderProvider, batchBuilderProvider); + DatabaseComponent db = createDatabaseComponent(database, cleaner); try { db.generateBundle(contactId, bundleBuilder); @@ -543,72 +460,43 @@ public abstract class DatabaseComponentTest extends TestCase { @SuppressWarnings("unchecked") final Database<Object> database = context.mock(Database.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 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() {{ allowing(database).startTransaction(); will(returnValue(txn)); allowing(database).commitTransaction(txn); allowing(database).containsContact(txn, contactId); will(returnValue(true)); - // Build the header - oneOf(headerBuilderProvider).get(); - will(returnValue(headerBuilder)); // Add acks to the header oneOf(database).removeBatchesToAck(txn, contactId); will(returnValue(acks)); - oneOf(headerBuilder).addAcks(acks); // Add subscriptions to the header oneOf(database).getSubscriptions(txn); will(returnValue(subs)); - oneOf(headerBuilder).addSubscriptions(subs); // Add transports to the header oneOf(database).getTransports(txn); will(returnValue(transports)); - oneOf(headerBuilder).addTransports(transports); // Build the header - oneOf(headerBuilder).build(); - will(returnValue(header)); - oneOf(bundleWriter).getCapacity(); - will(returnValue(1024L * 1024L)); - oneOf(header).getSize(); - will(returnValue(headerSize)); - oneOf(bundleWriter).addHeader(header); + oneOf(bundleWriter).addHeader(acks, subs, transports); + will(returnValue(bundleId)); // Add a batch to the bundle + oneOf(bundleWriter).getRemainingCapacity(); + will(returnValue(1024L * 1024L - headerSize)); oneOf(database).getSendableMessages(txn, contactId, Batch.MAX_SIZE - headerSize); will(returnValue(messages)); - oneOf(batchBuilderProvider).get(); - will(returnValue(batchBuilder)); oneOf(database).getMessage(txn, messageId); will(returnValue(message)); - oneOf(batchBuilder).addMessage(message); - oneOf(batchBuilder).build(); - will(returnValue(batch)); - // Record the batch as outstanding - oneOf(batch).getId(); + // Add the batch to the bundle + oneOf(bundleWriter).addBatch(Collections.singletonList(message)); will(returnValue(batchId)); + // Record the outstanding batch oneOf(database).addOutstandingBatch( txn, contactId, batchId, messages); - // Add the batch to the bundle - oneOf(bundleWriter).addBatch(batch); - // Check whether to add another batch - oneOf(batch).getSize(); - will(returnValue((long) message.getSize())); - // No, just send the bundle - oneOf(bundleWriter).close(); + // Send the bundle + oneOf(bundleWriter).finish(); }}); - DatabaseComponent db = createDatabaseComponent(database, cleaner, - headerBuilderProvider, batchBuilderProvider); + DatabaseComponent db = createDatabaseComponent(database, cleaner); db.generateBundle(contactId, bundleWriter); @@ -622,12 +510,6 @@ public abstract class DatabaseComponentTest extends TestCase { @SuppressWarnings("unchecked") final Database<Object> database = context.mock(Database.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); context.checking(new Expectations() {{ // Check that the contact is still in the DB @@ -637,8 +519,7 @@ public abstract class DatabaseComponentTest extends TestCase { will(returnValue(false)); oneOf(database).commitTransaction(txn); }}); - DatabaseComponent db = createDatabaseComponent(database, cleaner, - headerBuilderProvider, batchBuilderProvider); + DatabaseComponent db = createDatabaseComponent(database, cleaner); try { db.receiveBundle(contactId, bundleReader); @@ -654,12 +535,6 @@ public abstract class DatabaseComponentTest extends TestCase { @SuppressWarnings("unchecked") final Database<Object> database = context.mock(Database.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 Header header = context.mock(Header.class); final Batch batch = context.mock(Batch.class); @@ -702,7 +577,7 @@ public abstract class DatabaseComponentTest extends TestCase { // Any more batches? Nope oneOf(bundleReader).getNextBatch(); will(returnValue(null)); - oneOf(bundleReader).close(); + oneOf(bundleReader).finish(); // Lost batches oneOf(header).getId(); will(returnValue(bundleId)); @@ -710,8 +585,7 @@ public abstract class DatabaseComponentTest extends TestCase { will(returnValue(Collections.singleton(batchId))); oneOf(database).removeLostBatch(txn, contactId, batchId); }}); - DatabaseComponent db = createDatabaseComponent(database, cleaner, - headerBuilderProvider, batchBuilderProvider); + DatabaseComponent db = createDatabaseComponent(database, cleaner); db.receiveBundle(contactId, bundleReader); diff --git a/test/net/sf/briar/db/H2DatabaseTest.java b/test/net/sf/briar/db/H2DatabaseTest.java index 9596a1930d3822de7e0c32901d65a50d0fcc0618..18253c000d2181623bdfa06289435875bd4181f1 100644 --- a/test/net/sf/briar/db/H2DatabaseTest.java +++ b/test/net/sf/briar/db/H2DatabaseTest.java @@ -815,8 +815,8 @@ public class H2DatabaseTest extends TestCase { private static class TestMessageFactory implements MessageFactory { public Message createMessage(MessageId id, MessageId parent, - GroupId group, AuthorId author, long timestamp, byte[] body) { - return new MessageImpl(id, parent, group, author, timestamp, body); + GroupId group, AuthorId author, long timestamp, byte[] raw) { + return new MessageImpl(id, parent, group, author, timestamp, raw); } } } diff --git a/test/net/sf/briar/db/ReadWriteLockDatabaseComponentTest.java b/test/net/sf/briar/db/ReadWriteLockDatabaseComponentTest.java index ccc1a714d3f4064b2514c3f1b8d659a9243238b6..b0d5291f5aaa22087ef56c589a0ec38ca5907d31 100644 --- a/test/net/sf/briar/db/ReadWriteLockDatabaseComponentTest.java +++ b/test/net/sf/briar/db/ReadWriteLockDatabaseComponentTest.java @@ -1,29 +1,19 @@ package net.sf.briar.db; 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 extends DatabaseComponentImplTest { @Override protected <T> DatabaseComponent createDatabaseComponent( - Database<T> database, DatabaseCleaner cleaner, - Provider<HeaderBuilder> headerBuilderProvider, - Provider<BatchBuilder> batchBuilderProvider) { - return createDatabaseComponentImpl(database, cleaner, - headerBuilderProvider, batchBuilderProvider); + Database<T> database, DatabaseCleaner cleaner) { + return createDatabaseComponentImpl(database, cleaner); } @Override protected <T> DatabaseComponentImpl<T> createDatabaseComponentImpl( - Database<T> database, DatabaseCleaner cleaner, - Provider<HeaderBuilder> headerBuilderProvider, - Provider<BatchBuilder> batchBuilderProvider) { - return new ReadWriteLockDatabaseComponent<T>(database, cleaner, - headerBuilderProvider, batchBuilderProvider); + Database<T> database, DatabaseCleaner cleaner) { + return new ReadWriteLockDatabaseComponent<T>(database, cleaner); } } diff --git a/test/net/sf/briar/db/SynchronizedDatabaseComponentTest.java b/test/net/sf/briar/db/SynchronizedDatabaseComponentTest.java index 5c8f576c3d849b5aeae43d770cabfe266c2f6954..4c24f1d0edaf721cc835833d2e83c68fe5727f5e 100644 --- a/test/net/sf/briar/db/SynchronizedDatabaseComponentTest.java +++ b/test/net/sf/briar/db/SynchronizedDatabaseComponentTest.java @@ -1,29 +1,19 @@ package net.sf.briar.db; 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 extends DatabaseComponentImplTest { @Override protected <T> DatabaseComponent createDatabaseComponent( - Database<T> database, DatabaseCleaner cleaner, - Provider<HeaderBuilder> headerBuilderProvider, - Provider<BatchBuilder> batchBuilderProvider) { - return createDatabaseComponentImpl(database, cleaner, - headerBuilderProvider, batchBuilderProvider); + Database<T> database, DatabaseCleaner cleaner) { + return createDatabaseComponentImpl(database, cleaner); } @Override protected <T> DatabaseComponentImpl<T> createDatabaseComponentImpl( - Database<T> database, DatabaseCleaner cleaner, - Provider<HeaderBuilder> headerBuilderProvider, - Provider<BatchBuilder> batchBuilderProvider) { - return new SynchronizedDatabaseComponent<T>(database, cleaner, - headerBuilderProvider, batchBuilderProvider); + Database<T> database, DatabaseCleaner cleaner) { + return new SynchronizedDatabaseComponent<T>(database, cleaner); } } diff --git a/test/net/sf/briar/protocol/BundleReadWriteTest.java b/test/net/sf/briar/protocol/BundleReadWriteTest.java index 8a8851f925b18bb2224e75f557622d1d7566b71e..5460942084622306a6ddfb9852e5a2dd11a52e21 100644 --- a/test/net/sf/briar/protocol/BundleReadWriteTest.java +++ b/test/net/sf/briar/protocol/BundleReadWriteTest.java @@ -24,21 +24,17 @@ import junit.framework.TestCase; import net.sf.briar.TestUtils; import net.sf.briar.api.crypto.KeyParser; 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.BundleWriter; 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.MessageEncoder; import net.sf.briar.api.protocol.MessageId; import net.sf.briar.api.protocol.MessageParser; 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.Writer; import net.sf.briar.api.serial.WriterFactory; import net.sf.briar.serial.ReaderFactoryImpl; import net.sf.briar.serial.WriterFactoryImpl; @@ -47,8 +43,6 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; -import com.google.inject.Provider; - public class BundleReadWriteTest extends TestCase { private static final String SIGNATURE_ALGO = "SHA256withRSA"; @@ -74,7 +68,7 @@ public class BundleReadWriteTest extends TestCase { private final KeyPair keyPair; private final Signature sig; - private final MessageDigest digest; + private final MessageDigest dig; private final KeyParser keyParser; private final Message message; @@ -82,7 +76,7 @@ public class BundleReadWriteTest extends TestCase { super(); keyPair = KeyPairGenerator.getInstance(KEY_PAIR_ALGO).generateKeyPair(); sig = Signature.getInstance(SIGNATURE_ALGO); - digest = MessageDigest.getInstance(DIGEST_ALGO); + dig = MessageDigest.getInstance(DIGEST_ALGO); final KeyFactory keyFactory = KeyFactory.getInstance(KEY_PAIR_ALGO); keyParser = new KeyParser() { public PublicKey parsePublicKey(byte[] encodedKey) @@ -91,8 +85,8 @@ public class BundleReadWriteTest extends TestCase { return keyFactory.generatePublic(e); } }; - assertEquals(digest.getDigestLength(), UniqueId.LENGTH); - MessageEncoder messageEncoder = new MessageEncoderImpl(sig, digest, wf); + assertEquals(dig.getDigestLength(), UniqueId.LENGTH); + MessageEncoder messageEncoder = new MessageEncoderImpl(sig, dig, wf); message = messageEncoder.encodeMessage(MessageId.NONE, sub, nick, keyPair, messageBody.getBytes("UTF-8")); } @@ -104,23 +98,13 @@ public class BundleReadWriteTest extends TestCase { @Test 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); - Writer writer = new WriterFactoryImpl().createWriter(out); - BundleWriter w = new BundleWriterImpl(writer, capacity); + BundleWriter w = new BundleWriterImpl(out, wf, keyPair.getPrivate(), + sig, dig, capacity); - w.addHeader(header); - w.addBatch(batch); - w.close(); + w.addHeader(acks, subs, transports); + w.addBatch(Collections.singleton(message)); + w.finish(); assertTrue(bundle.exists()); assertTrue(bundle.length() > message.getSize()); @@ -132,24 +116,11 @@ public class BundleReadWriteTest extends TestCase { testWriteBundle(); MessageParser messageParser = - new MessageParserImpl(keyParser, sig, digest, 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); - } - }; - + new MessageParserImpl(keyParser, sig, dig, rf); FileInputStream in = new FileInputStream(bundle); - Reader reader = new ReaderFactoryImpl().createReader(in); - BundleReader r = new BundleReaderImpl(reader, bundle.length(), - messageParser, headerBuilderProvider, batchBuilderProvider); + BundleReader r = new BundleReaderImpl(in, rf, keyPair.getPublic(), sig, + dig, messageParser, new HeaderFactoryImpl(), + new BatchFactoryImpl()); Header h = r.getHeader(); assertEquals(acks, h.getAcks()); @@ -167,7 +138,7 @@ public class BundleReadWriteTest extends TestCase { assertTrue(Arrays.equals(message.getBytes(), m.getBytes())); assertFalse(i.hasNext()); assertNull(r.getNextBatch()); - r.close(); + r.finish(); } @@ -184,24 +155,11 @@ public class BundleReadWriteTest extends TestCase { f.close(); MessageParser messageParser = - new MessageParserImpl(keyParser, sig, digest, 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); - } - }; - + new MessageParserImpl(keyParser, sig, dig, rf); FileInputStream in = new FileInputStream(bundle); - Reader reader = new ReaderFactoryImpl().createReader(in); - BundleReader r = new BundleReaderImpl(reader, bundle.length(), - messageParser, headerBuilderProvider, batchBuilderProvider); + BundleReader r = new BundleReaderImpl(in, rf, keyPair.getPublic(), sig, + dig, messageParser, new HeaderFactoryImpl(), + new BatchFactoryImpl()); Header h = r.getHeader(); assertEquals(acks, h.getAcks()); @@ -211,7 +169,7 @@ public class BundleReadWriteTest extends TestCase { r.getNextBatch(); assertTrue(false); } catch(GeneralSecurityException expected) {} - r.close(); + r.finish(); } @After diff --git a/test/net/sf/briar/protocol/BundleReaderTest.java b/test/net/sf/briar/protocol/BundleReaderTest.java deleted file mode 100644 index 1923416f4b0095be88ac66b0b3740c012c9aaedd..0000000000000000000000000000000000000000 --- a/test/net/sf/briar/protocol/BundleReaderTest.java +++ /dev/null @@ -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(); - } -} diff --git a/test/net/sf/briar/protocol/BundleWriterTest.java b/test/net/sf/briar/protocol/BundleWriterTest.java deleted file mode 100644 index 2ba6fedd38cecfaabee90ab9d9cf5aa2313af366..0000000000000000000000000000000000000000 --- a/test/net/sf/briar/protocol/BundleWriterTest.java +++ /dev/null @@ -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(); - } -} diff --git a/test/net/sf/briar/protocol/SigningStreamTest.java b/test/net/sf/briar/protocol/SigningStreamTest.java new file mode 100644 index 0000000000000000000000000000000000000000..e1b68e23ae2087e314f844966d590fe1195334a5 --- /dev/null +++ b/test/net/sf/briar/protocol/SigningStreamTest.java @@ -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())); + } +} \ No newline at end of file diff --git a/test/net/sf/briar/protocol/TestRaw.java b/test/net/sf/briar/protocol/TestRaw.java deleted file mode 100644 index f9f685b3632bfaca3244040a4c2c7d05cdcbefa3..0000000000000000000000000000000000000000 --- a/test/net/sf/briar/protocol/TestRaw.java +++ /dev/null @@ -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; - } -}