From 3d549ea6ac9a4a91ad210371b36093781be964bb Mon Sep 17 00:00:00 2001 From: akwizgran <akwizgran@users.sourceforge.net> Date: Tue, 12 Jul 2011 16:50:20 +0100 Subject: [PATCH] Builders for incoming and outgoing headers and batches. The protocol and serial components can now be used to serialise, sign, deserialise and verify real bundles (except for message parsing). --- .../sf/briar/api/db/DatabaseComponent.java | 10 +- .../sf/briar/api/protocol/BatchBuilder.java | 4 +- api/net/sf/briar/api/protocol/Bundle.java | 21 --- .../sf/briar/api/protocol/BundleReader.java | 25 +++ .../{BundleBuilder.java => BundleWriter.java} | 10 +- .../sf/briar/api/protocol/HeaderBuilder.java | 12 +- api/net/sf/briar/api/protocol/Message.java | 7 +- api/net/sf/briar/api/serial/Reader.java | 1 + api/net/sf/briar/api/serial/Writer.java | 4 +- components/net/sf/briar/db/JdbcDatabase.java | 2 +- .../db/ReadWriteLockDatabaseComponent.java | 99 ++++++----- .../db/SynchronizedDatabaseComponent.java | 89 +++++----- .../sf/briar/protocol/BatchBuilderImpl.java | 44 +++++ ...undleReader.java => BundleReaderImpl.java} | 22 ++- ...undleWriter.java => BundleWriterImpl.java} | 10 +- .../net/sf/briar/protocol/FileBundle.java | 30 ---- .../sf/briar/protocol/FileBundleBuilder.java | 41 ----- .../sf/briar/protocol/HeaderBuilderImpl.java | 63 +++++++ .../briar/protocol/IncomingBatchBuilder.java | 40 +++++ .../briar/protocol/IncomingHeaderBuilder.java | 47 ++++++ .../net/sf/briar/protocol/MessageImpl.java | 2 +- .../briar/protocol/OutgoingBatchBuilder.java | 37 +++++ .../briar/protocol/OutgoingHeaderBuilder.java | 44 +++++ .../sf/briar/serial/ReaderFactoryImpl.java | 2 +- .../net/sf/briar/serial/ReaderImpl.java | 4 + .../sf/briar/serial/WriterFactoryImpl.java | 2 +- .../net/sf/briar/serial/WriterImpl.java | 10 +- test/build.xml | 1 + .../sf/briar/db/DatabaseComponentTest.java | 47 +++--- test/net/sf/briar/db/H2DatabaseTest.java | 2 +- .../briar/protocol/BundleReadWriteTest.java | 156 ++++++++++++++++++ .../sf/briar/protocol/BundleReaderTest.java | 59 ++++--- .../sf/briar/protocol/BundleWriterTest.java | 28 ++-- 33 files changed, 692 insertions(+), 283 deletions(-) delete mode 100644 api/net/sf/briar/api/protocol/Bundle.java create mode 100644 api/net/sf/briar/api/protocol/BundleReader.java rename api/net/sf/briar/api/protocol/{BundleBuilder.java => BundleWriter.java} (60%) create mode 100644 components/net/sf/briar/protocol/BatchBuilderImpl.java rename components/net/sf/briar/protocol/{BundleReader.java => BundleReaderImpl.java} (84%) rename components/net/sf/briar/protocol/{BundleWriter.java => BundleWriterImpl.java} (86%) delete mode 100644 components/net/sf/briar/protocol/FileBundle.java delete mode 100644 components/net/sf/briar/protocol/FileBundleBuilder.java create mode 100644 components/net/sf/briar/protocol/HeaderBuilderImpl.java create mode 100644 components/net/sf/briar/protocol/IncomingBatchBuilder.java create mode 100644 components/net/sf/briar/protocol/IncomingHeaderBuilder.java create mode 100644 components/net/sf/briar/protocol/OutgoingBatchBuilder.java create mode 100644 components/net/sf/briar/protocol/OutgoingHeaderBuilder.java create mode 100644 test/net/sf/briar/protocol/BundleReadWriteTest.java diff --git a/api/net/sf/briar/api/db/DatabaseComponent.java b/api/net/sf/briar/api/db/DatabaseComponent.java index f662e85b60..bd7075d4a3 100644 --- a/api/net/sf/briar/api/db/DatabaseComponent.java +++ b/api/net/sf/briar/api/db/DatabaseComponent.java @@ -1,15 +1,15 @@ package net.sf.briar.api.db; import java.io.IOException; -import java.security.SignatureException; +import java.security.GeneralSecurityException; import java.util.Map; import java.util.Set; import net.sf.briar.api.ContactId; import net.sf.briar.api.Rating; import net.sf.briar.api.protocol.AuthorId; -import net.sf.briar.api.protocol.Bundle; -import net.sf.briar.api.protocol.BundleBuilder; +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.Message; @@ -51,7 +51,7 @@ public interface DatabaseComponent { * Generates a bundle of acknowledgements, subscriptions, and batches of * messages for the given contact. */ - Bundle generateBundle(ContactId c, BundleBuilder bundleBuilder) throws DbException, IOException, SignatureException; + void generateBundle(ContactId c, BundleWriter bundleBuilder) throws DbException, IOException, GeneralSecurityException; /** Returns the IDs of all contacts. */ Set<ContactId> getContacts() throws DbException; @@ -73,7 +73,7 @@ public interface DatabaseComponent { * messages received from the given contact. Some or all of the messages * in the bundle may be stored. */ - void receiveBundle(ContactId c, Bundle b) throws DbException, IOException, SignatureException; + void receiveBundle(ContactId c, BundleReader b) throws DbException, IOException, GeneralSecurityException; /** Removes a contact (and all associated state) from the database. */ void removeContact(ContactId c) throws DbException; diff --git a/api/net/sf/briar/api/protocol/BatchBuilder.java b/api/net/sf/briar/api/protocol/BatchBuilder.java index 992657c9a9..fd17670c0d 100644 --- a/api/net/sf/briar/api/protocol/BatchBuilder.java +++ b/api/net/sf/briar/api/protocol/BatchBuilder.java @@ -1,5 +1,7 @@ package net.sf.briar.api.protocol; +import java.io.IOException; +import java.security.InvalidKeyException; import java.security.SignatureException; public interface BatchBuilder { @@ -11,5 +13,5 @@ public interface BatchBuilder { void setSignature(byte[] sig); /** Builds and returns the batch. */ - Batch build() throws SignatureException; + Batch build() throws IOException, SignatureException, InvalidKeyException; } diff --git a/api/net/sf/briar/api/protocol/Bundle.java b/api/net/sf/briar/api/protocol/Bundle.java deleted file mode 100644 index 82ff0f86d9..0000000000 --- a/api/net/sf/briar/api/protocol/Bundle.java +++ /dev/null @@ -1,21 +0,0 @@ -package net.sf.briar.api.protocol; - -import java.io.IOException; -import java.security.SignatureException; - -/** - * A bundle of acknowledgements, subscriptions, transport details and batches. - */ -public interface Bundle { - - /** Returns the size of the serialised bundle in bytes. */ - long getSize() throws IOException; - - /** Returns the bundle's header. */ - Header getHeader() throws IOException, SignatureException; - - /** - * Returns the next batch of messages, or null if there are no more batches. - */ - Batch getNextBatch() throws IOException, SignatureException; -} diff --git a/api/net/sf/briar/api/protocol/BundleReader.java b/api/net/sf/briar/api/protocol/BundleReader.java new file mode 100644 index 0000000000..b5e6390437 --- /dev/null +++ b/api/net/sf/briar/api/protocol/BundleReader.java @@ -0,0 +1,25 @@ +package net.sf.briar.api.protocol; + +import java.io.IOException; +import java.security.GeneralSecurityException; + +/** + * An interface for reading a bundle of acknowledgements, subscriptions, + * transport details and batches. + */ +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; + + /** + * Returns the next batch of messages, or null if there are no more batches. + */ + Batch getNextBatch() throws IOException, GeneralSecurityException; + + /** Finishes reading the bundle. */ + void close() throws IOException; +} diff --git a/api/net/sf/briar/api/protocol/BundleBuilder.java b/api/net/sf/briar/api/protocol/BundleWriter.java similarity index 60% rename from api/net/sf/briar/api/protocol/BundleBuilder.java rename to api/net/sf/briar/api/protocol/BundleWriter.java index 7f7c837719..730a133ae6 100644 --- a/api/net/sf/briar/api/protocol/BundleBuilder.java +++ b/api/net/sf/briar/api/protocol/BundleWriter.java @@ -2,7 +2,11 @@ package net.sf.briar.api.protocol; import java.io.IOException; -public interface BundleBuilder { +/** + * An interface for writing a bundle of acknowledgements, subscriptions, + * transport details and batches. + */ +public interface BundleWriter { /** Returns the bundle's capacity in bytes. */ long getCapacity() throws IOException; @@ -13,6 +17,6 @@ public interface BundleBuilder { /** Adds a batch of messages to the bundle. */ void addBatch(Batch b) throws IOException; - /** Builds and returns the bundle. */ - Bundle build() throws IOException; + /** Finishes writing the bundle. */ + void close() throws IOException; } diff --git a/api/net/sf/briar/api/protocol/HeaderBuilder.java b/api/net/sf/briar/api/protocol/HeaderBuilder.java index 4c00f8218f..9c13b889f3 100644 --- a/api/net/sf/briar/api/protocol/HeaderBuilder.java +++ b/api/net/sf/briar/api/protocol/HeaderBuilder.java @@ -1,24 +1,24 @@ package net.sf.briar.api.protocol; import java.io.IOException; +import java.security.InvalidKeyException; import java.security.SignatureException; import java.util.Map; -import java.util.Set; public interface HeaderBuilder { /** Adds acknowledgements to the header. */ - void addAcks(Set<BatchId> acks) throws IOException; + void addAcks(Iterable<BatchId> acks); /** Adds subscriptions to the header. */ - void addSubscriptions(Set<GroupId> subs) throws IOException; + void addSubscriptions(Iterable<GroupId> subs); /** Adds transport details to the header. */ - void addTransports(Map<String, String> transports) throws IOException; + void addTransports(Map<String, String> transports); /** Sets the sender's signature over the contents of the header. */ - void setSignature(byte[] sig) throws IOException; + void setSignature(byte[] sig); /** Builds and returns the header. */ - Header build() throws SignatureException; + Header build() throws IOException, SignatureException, InvalidKeyException; } diff --git a/api/net/sf/briar/api/protocol/Message.java b/api/net/sf/briar/api/protocol/Message.java index 8ed42d12d2..715f1115ae 100644 --- a/api/net/sf/briar/api/protocol/Message.java +++ b/api/net/sf/briar/api/protocol/Message.java @@ -1,6 +1,8 @@ package net.sf.briar.api.protocol; -public interface Message { +import net.sf.briar.api.serial.Raw; + +public interface Message extends Raw { /** Returns the message's unique identifier. */ MessageId getId(); @@ -22,7 +24,4 @@ public interface Message { /** Returns the size of the message in bytes. */ int getSize(); - - /** Returns the message in wire format. */ - byte[] getBody(); } \ No newline at end of file diff --git a/api/net/sf/briar/api/serial/Reader.java b/api/net/sf/briar/api/serial/Reader.java index 3a92c90a16..47bc385919 100644 --- a/api/net/sf/briar/api/serial/Reader.java +++ b/api/net/sf/briar/api/serial/Reader.java @@ -9,6 +9,7 @@ public interface Reader { boolean eof() throws IOException; void setReadLimit(long limit); void resetReadLimit(); + void close() throws IOException; boolean hasBoolean() throws IOException; boolean readBoolean() throws IOException; diff --git a/api/net/sf/briar/api/serial/Writer.java b/api/net/sf/briar/api/serial/Writer.java index 103b8a5f0c..41f273333b 100644 --- a/api/net/sf/briar/api/serial/Writer.java +++ b/api/net/sf/briar/api/serial/Writer.java @@ -6,6 +6,8 @@ import java.util.Map; public interface Writer { + void close() throws IOException; + void writeBoolean(boolean b) throws IOException; void writeUint7(byte b) throws IOException; @@ -31,6 +33,4 @@ public interface Writer { void writeMapEnd() throws IOException; void writeNull() throws IOException; - - void close() throws IOException; } diff --git a/components/net/sf/briar/db/JdbcDatabase.java b/components/net/sf/briar/db/JdbcDatabase.java index 950547b50d..2c6c555263 100644 --- a/components/net/sf/briar/db/JdbcDatabase.java +++ b/components/net/sf/briar/db/JdbcDatabase.java @@ -467,7 +467,7 @@ abstract class JdbcDatabase implements Database<Connection> { ps.setBytes(4, m.getAuthor().getBytes()); ps.setLong(5, m.getTimestamp()); ps.setInt(6, m.getSize()); - ps.setBlob(7, new ByteArrayInputStream(m.getBody())); + ps.setBlob(7, new ByteArrayInputStream(m.getBytes())); ps.setInt(8, 0); int rowsAffected = ps.executeUpdate(); assert rowsAffected == 1; diff --git a/components/net/sf/briar/db/ReadWriteLockDatabaseComponent.java b/components/net/sf/briar/db/ReadWriteLockDatabaseComponent.java index 8ab59f9335..f9971cfc06 100644 --- a/components/net/sf/briar/db/ReadWriteLockDatabaseComponent.java +++ b/components/net/sf/briar/db/ReadWriteLockDatabaseComponent.java @@ -1,6 +1,7 @@ package net.sf.briar.db; import java.io.IOException; +import java.security.GeneralSecurityException; import java.security.SignatureException; import java.util.HashSet; import java.util.Iterator; @@ -18,8 +19,9 @@ 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.Bundle; -import net.sf.briar.api.protocol.BundleBuilder; +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; @@ -190,8 +192,8 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { } } - public Bundle generateBundle(ContactId c, BundleBuilder b) - throws DbException, IOException, SignatureException { + public void generateBundle(ContactId c, BundleWriter b) throws DbException, + IOException, GeneralSecurityException { if(LOG.isLoggable(Level.FINE)) LOG.fine("Generating bundle for " + c); HeaderBuilder h; // Add acks @@ -280,15 +282,13 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { // more messages trickling in but we can't wait forever if(size * 2 < Batch.MAX_SIZE) break; } - Bundle bundle = b.build(); - if(LOG.isLoggable(Level.FINE)) - LOG.fine("Bundle generated, " + bundle.getSize() + " bytes"); + b.close(); + if(LOG.isLoggable(Level.FINE)) LOG.fine("Bundle generated"); System.gc(); - return bundle; } private Batch fillBatch(ContactId c, long capacity) throws DbException, - SignatureException { + IOException, GeneralSecurityException { contactLock.readLock().lock(); try { if(!containsContact(c)) throw new NoSuchContactException(); @@ -441,8 +441,8 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { } } - public void receiveBundle(ContactId c, Bundle b) throws DbException, - IOException, SignatureException { + 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"); @@ -529,52 +529,64 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { } // Store the messages int batches = 0; - for(Batch batch = b.getNextBatch(); batch != null; batch = b.getNextBatch()) { + Batch batch = null; + while((batch = b.getNextBatch()) != null) { + storeBatch(c, batch); batches++; - waitForPermissionToWrite(); - contactLock.readLock().lock(); + } + if(LOG.isLoggable(Level.FINE)) + LOG.fine("Received " + batches + " batches"); + b.close(); + retransmitLostBatches(c, h.getId()); + System.gc(); + } + + private void storeBatch(ContactId c, Batch b) throws DbException { + waitForPermissionToWrite(); + contactLock.readLock().lock(); + try { + if(!containsContact(c)) throw new NoSuchContactException(); + messageLock.writeLock().lock(); try { - if(!containsContact(c)) throw new NoSuchContactException(); - messageLock.writeLock().lock(); + messageStatusLock.writeLock().lock(); try { - messageStatusLock.writeLock().lock(); + subscriptionLock.readLock().lock(); try { - subscriptionLock.readLock().lock(); + Txn txn = db.startTransaction(); try { - Txn txn = db.startTransaction(); - try { - int received = 0, stored = 0; - for(Message m : batch.getMessages()) { - received++; - GroupId g = m.getGroup(); - if(db.containsSubscription(txn, g)) { - if(storeMessage(txn, m, c)) stored++; - } + int received = 0, stored = 0; + for(Message m : b.getMessages()) { + received++; + GroupId g = m.getGroup(); + if(db.containsSubscription(txn, g)) { + if(storeMessage(txn, m, c)) stored++; } - if(LOG.isLoggable(Level.FINE)) - LOG.fine("Received " + received - + " messages, stored " + stored); - db.addBatchToAck(txn, c, batch.getId()); - db.commitTransaction(txn); - } catch(DbException e) { - db.abortTransaction(txn); - throw e; } - } finally { - subscriptionLock.readLock().unlock(); + if(LOG.isLoggable(Level.FINE)) + LOG.fine("Received " + received + + " messages, stored " + stored); + db.addBatchToAck(txn, c, b.getId()); + db.commitTransaction(txn); + } catch(DbException e) { + db.abortTransaction(txn); + throw e; } } finally { - messageStatusLock.writeLock().unlock(); + subscriptionLock.readLock().unlock(); } } finally { - messageLock.writeLock().unlock(); + messageStatusLock.writeLock().unlock(); } } finally { - contactLock.readLock().unlock(); + messageLock.writeLock().unlock(); } + } finally { + contactLock.readLock().unlock(); } - if(LOG.isLoggable(Level.FINE)) - LOG.fine("Received " + batches + " batches"); + } + + private void retransmitLostBatches(ContactId c, BundleId b) + throws DbException { // Find any lost batches that need to be retransmitted Set<BatchId> lost; contactLock.readLock().lock(); @@ -586,7 +598,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { try { Txn txn = db.startTransaction(); try { - lost = db.addReceivedBundle(txn, c, h.getId()); + lost = db.addReceivedBundle(txn, c, b); db.commitTransaction(txn); } catch(DbException e) { db.abortTransaction(txn); @@ -629,7 +641,6 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { contactLock.readLock().unlock(); } } - System.gc(); } public void removeContact(ContactId c) throws DbException { diff --git a/components/net/sf/briar/db/SynchronizedDatabaseComponent.java b/components/net/sf/briar/db/SynchronizedDatabaseComponent.java index df94720028..f7caf09107 100644 --- a/components/net/sf/briar/db/SynchronizedDatabaseComponent.java +++ b/components/net/sf/briar/db/SynchronizedDatabaseComponent.java @@ -1,6 +1,7 @@ package net.sf.briar.db; import java.io.IOException; +import java.security.GeneralSecurityException; import java.security.SignatureException; import java.util.HashSet; import java.util.Iterator; @@ -17,8 +18,9 @@ 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.Bundle; -import net.sf.briar.api.protocol.BundleBuilder; +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; @@ -143,8 +145,8 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { } } - public Bundle generateBundle(ContactId c, BundleBuilder b) - throws DbException, IOException, SignatureException { + public void generateBundle(ContactId c, BundleWriter b) throws DbException, + IOException, GeneralSecurityException { if(LOG.isLoggable(Level.FINE)) LOG.fine("Generating bundle for " + c); HeaderBuilder h; // Add acks @@ -215,15 +217,13 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { // more messages trickling in but we can't wait forever if(size * 2 < Batch.MAX_SIZE) break; } - Bundle bundle = b.build(); - if(LOG.isLoggable(Level.FINE)) - LOG.fine("Bundle generated, " + bundle.getSize() + " bytes"); + b.close(); + if(LOG.isLoggable(Level.FINE)) LOG.fine("Bundle generated"); System.gc(); - return bundle; } private Batch fillBatch(ContactId c, long capacity) throws DbException, - SignatureException { + IOException, GeneralSecurityException { synchronized(contactLock) { if(!containsContact(c)) throw new NoSuchContactException(); synchronized(messageLock) { @@ -335,8 +335,8 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { } } - public void receiveBundle(ContactId c, Bundle b) throws DbException, - IOException, SignatureException { + 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"); @@ -402,40 +402,52 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { } // Store the messages int batches = 0; - for(Batch batch = b.getNextBatch(); batch != null; batch = b.getNextBatch()) { + Batch batch = null; + while((batch = b.getNextBatch()) != null) { + storeBatch(c, batch); batches++; - waitForPermissionToWrite(); - synchronized(contactLock) { - if(!containsContact(c)) throw new NoSuchContactException(); - synchronized(messageLock) { - synchronized(messageStatusLock) { - synchronized(subscriptionLock) { - Txn txn = db.startTransaction(); - try { - int received = 0, stored = 0; - for(Message m : batch.getMessages()) { - received++; - GroupId g = m.getGroup(); - if(db.containsSubscription(txn, g)) { - if(storeMessage(txn, m, c)) stored++; - } + } + if(LOG.isLoggable(Level.FINE)) + LOG.fine("Received " + batches + " batches"); + b.close(); + retransmitLostBatches(c, h.getId()); + System.gc(); + } + + private void storeBatch(ContactId c, Batch b) throws DbException { + waitForPermissionToWrite(); + synchronized(contactLock) { + if(!containsContact(c)) throw new NoSuchContactException(); + synchronized(messageLock) { + synchronized(messageStatusLock) { + synchronized(subscriptionLock) { + Txn txn = db.startTransaction(); + try { + int received = 0, stored = 0; + for(Message m : b.getMessages()) { + received++; + GroupId g = m.getGroup(); + if(db.containsSubscription(txn, g)) { + if(storeMessage(txn, m, c)) stored++; } - if(LOG.isLoggable(Level.FINE)) - LOG.fine("Received " + received - + " messages, stored " + stored); - db.addBatchToAck(txn, c, batch.getId()); - db.commitTransaction(txn); - } catch(DbException e) { - db.abortTransaction(txn); - throw e; } + if(LOG.isLoggable(Level.FINE)) + LOG.fine("Received " + received + + " messages, stored " + stored); + db.addBatchToAck(txn, c, b.getId()); + db.commitTransaction(txn); + } catch(DbException e) { + db.abortTransaction(txn); + throw e; } } } } } - if(LOG.isLoggable(Level.FINE)) - LOG.fine("Received " + batches + " batches"); + } + + private void retransmitLostBatches(ContactId c, BundleId b) + throws DbException { // Find any lost batches that need to be retransmitted Set<BatchId> lost; synchronized(contactLock) { @@ -444,7 +456,7 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { synchronized(messageStatusLock) { Txn txn = db.startTransaction(); try { - lost = db.addReceivedBundle(txn, c, h.getId()); + lost = db.addReceivedBundle(txn, c, b); db.commitTransaction(txn); } catch(DbException e) { db.abortTransaction(txn); @@ -472,7 +484,6 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { } } } - System.gc(); } public void removeContact(ContactId c) throws DbException { diff --git a/components/net/sf/briar/protocol/BatchBuilderImpl.java b/components/net/sf/briar/protocol/BatchBuilderImpl.java new file mode 100644 index 0000000000..d1fabbeed4 --- /dev/null +++ b/components/net/sf/briar/protocol/BatchBuilderImpl.java @@ -0,0 +1,44 @@ +package net.sf.briar.protocol; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +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.Writer; +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; + + private 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); + } + + protected byte[] getSignableRepresentation() throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + Writer w = writerFactory.createWriter(out); + w.writeList(messages); + w.close(); + return out.toByteArray(); + } +} diff --git a/components/net/sf/briar/protocol/BundleReader.java b/components/net/sf/briar/protocol/BundleReaderImpl.java similarity index 84% rename from components/net/sf/briar/protocol/BundleReader.java rename to components/net/sf/briar/protocol/BundleReaderImpl.java index c3bcdf0ca5..ea397abdf2 100644 --- a/components/net/sf/briar/protocol/BundleReader.java +++ b/components/net/sf/briar/protocol/BundleReaderImpl.java @@ -1,7 +1,7 @@ package net.sf.briar.protocol; import java.io.IOException; -import java.security.SignatureException; +import java.security.GeneralSecurityException; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -10,7 +10,7 @@ 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.Bundle; +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; @@ -24,26 +24,32 @@ import net.sf.briar.api.serial.Reader; import com.google.inject.Provider; /** A bundle that deserialises its contents on demand using a reader. */ -abstract class BundleReader implements Bundle { +class BundleReaderImpl implements BundleReader { private static enum State { START, FIRST_BATCH, MORE_BATCHES, END }; private final Reader r; + private final long size; private final MessageParser messageParser; private final Provider<HeaderBuilder> headerBuilderProvider; private final Provider<BatchBuilder> batchBuilderProvider; private State state = State.START; - BundleReader(Reader r, MessageParser messageParser, + BundleReaderImpl(Reader r, long size, MessageParser messageParser, Provider<HeaderBuilder> headerBuilderProvider, Provider<BatchBuilder> batchBuilderProvider) { this.r = r; + this.size = size; this.messageParser = messageParser; this.headerBuilderProvider = headerBuilderProvider; this.batchBuilderProvider = batchBuilderProvider; } - public Header getHeader() throws IOException, SignatureException { + public long getSize() { + return size; + } + + public Header getHeader() throws IOException, GeneralSecurityException { if(state != State.START) throw new IllegalStateException(); r.setReadLimit(Header.MAX_SIZE); Set<BatchId> acks = new HashSet<BatchId>(); @@ -69,7 +75,7 @@ abstract class BundleReader implements Bundle { return h.build(); } - public Batch getNextBatch() throws IOException, SignatureException { + public Batch getNextBatch() throws IOException, GeneralSecurityException { if(state == State.FIRST_BATCH) { r.readListStart(); state = State.MORE_BATCHES; @@ -91,4 +97,8 @@ abstract class BundleReader implements Bundle { b.setSignature(sig); return b.build(); } + + public void close() throws IOException { + r.close(); + } } diff --git a/components/net/sf/briar/protocol/BundleWriter.java b/components/net/sf/briar/protocol/BundleWriterImpl.java similarity index 86% rename from components/net/sf/briar/protocol/BundleWriter.java rename to components/net/sf/briar/protocol/BundleWriterImpl.java index ccd5894752..ba3f3f8306 100644 --- a/components/net/sf/briar/protocol/BundleWriter.java +++ b/components/net/sf/briar/protocol/BundleWriterImpl.java @@ -4,14 +4,14 @@ import java.io.IOException; import net.sf.briar.api.protocol.Batch; import net.sf.briar.api.protocol.BatchId; -import net.sf.briar.api.protocol.BundleBuilder; +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; /** A bundle builder that serialises its contents using a writer. */ -abstract class BundleWriter implements BundleBuilder { +class BundleWriterImpl implements BundleWriter { private static enum State { START, FIRST_BATCH, MORE_BATCHES, END }; @@ -19,7 +19,7 @@ abstract class BundleWriter implements BundleBuilder { private final long capacity; private State state = State.START; - BundleWriter(Writer w, long capacity) { + BundleWriterImpl(Writer w, long capacity) { this.w = w; this.capacity = capacity; } @@ -48,12 +48,12 @@ abstract class BundleWriter implements BundleBuilder { } if(state != State.MORE_BATCHES) throw new IllegalStateException(); w.writeListStart(); - for(Message m : b.getMessages()) w.writeRaw(m.getBody()); + for(Message m : b.getMessages()) w.writeRaw(m.getBytes()); w.writeListEnd(); w.writeRaw(b.getSignature()); } - void close() throws IOException { + public void close() throws IOException { if(state == State.FIRST_BATCH) { w.writeListStart(); state = State.MORE_BATCHES; diff --git a/components/net/sf/briar/protocol/FileBundle.java b/components/net/sf/briar/protocol/FileBundle.java deleted file mode 100644 index 930f8699fe..0000000000 --- a/components/net/sf/briar/protocol/FileBundle.java +++ /dev/null @@ -1,30 +0,0 @@ -package net.sf.briar.protocol; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; - -import net.sf.briar.api.protocol.BatchBuilder; -import net.sf.briar.api.protocol.HeaderBuilder; -import net.sf.briar.api.protocol.MessageParser; -import net.sf.briar.api.serial.ReaderFactory; - -import com.google.inject.Provider; - -class FileBundle extends BundleReader { - - private final File file; - - FileBundle(File file, ReaderFactory readerFactory, - MessageParser messageParser, - Provider<HeaderBuilder> headerBuilderProvider, - Provider<BatchBuilder> batchBuilderProvider) throws IOException { - super(readerFactory.createReader(new FileInputStream(file)), - messageParser, headerBuilderProvider, batchBuilderProvider); - this.file = file; - } - - public long getSize() throws IOException { - return file.length(); - } -} diff --git a/components/net/sf/briar/protocol/FileBundleBuilder.java b/components/net/sf/briar/protocol/FileBundleBuilder.java deleted file mode 100644 index 085f637f74..0000000000 --- a/components/net/sf/briar/protocol/FileBundleBuilder.java +++ /dev/null @@ -1,41 +0,0 @@ -package net.sf.briar.protocol; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; - -import net.sf.briar.api.protocol.BatchBuilder; -import net.sf.briar.api.protocol.Bundle; -import net.sf.briar.api.protocol.HeaderBuilder; -import net.sf.briar.api.protocol.MessageParser; -import net.sf.briar.api.serial.ReaderFactory; -import net.sf.briar.api.serial.WriterFactory; - -import com.google.inject.Provider; - -public class FileBundleBuilder extends BundleWriter { - - private final File file; - private final ReaderFactory readerFactory; - private final MessageParser messageParser; - private final Provider<HeaderBuilder> headerBuilderProvider; - private final Provider<BatchBuilder> batchBuilderProvider; - - FileBundleBuilder(File file, long capacity, WriterFactory writerFactory, - ReaderFactory readerFactory, MessageParser messageParser, - Provider<HeaderBuilder> headerBuilderProvider, - Provider<BatchBuilder> batchBuilderProvider) throws IOException { - super(writerFactory.createWriter(new FileOutputStream(file)), capacity); - this.file = file; - this.readerFactory = readerFactory; - this.messageParser = messageParser; - this.headerBuilderProvider = headerBuilderProvider; - this.batchBuilderProvider = batchBuilderProvider; - } - - public Bundle build() throws IOException { - super.close(); - return new FileBundle(file, readerFactory, messageParser, - headerBuilderProvider, batchBuilderProvider); - } -} diff --git a/components/net/sf/briar/protocol/HeaderBuilderImpl.java b/components/net/sf/briar/protocol/HeaderBuilderImpl.java new file mode 100644 index 0000000000..78f19923a3 --- /dev/null +++ b/components/net/sf/briar/protocol/HeaderBuilderImpl.java @@ -0,0 +1,63 @@ +package net.sf.briar.protocol; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +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.Writer; +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; + + private 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)); + } + } + + protected byte[] getSignableRepresentation() throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + Writer w = writerFactory.createWriter(out); + w.writeList(acks); + w.writeList(subs); + w.writeMap(transports); + w.close(); + return out.toByteArray(); + } +} diff --git a/components/net/sf/briar/protocol/IncomingBatchBuilder.java b/components/net/sf/briar/protocol/IncomingBatchBuilder.java new file mode 100644 index 0000000000..5cce8fa658 --- /dev/null +++ b/components/net/sf/briar/protocol/IncomingBatchBuilder.java @@ -0,0 +1,40 @@ +package net.sf.briar.protocol; + +import java.io.IOException; +import java.security.InvalidKeyException; +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.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, SignatureException, + InvalidKeyException { + if(sig == null) throw new IllegalStateException(); + byte[] raw = getSignableRepresentation(); + signature.initVerify(keyPair.getPublic()); + signature.update(raw); + signature.verify(sig); + messageDigest.reset(); + messageDigest.update(raw); + messageDigest.update(sig); + byte[] hash = messageDigest.digest(); + return new BatchImpl(new BatchId(hash), raw.length, messages, sig); + } +} diff --git a/components/net/sf/briar/protocol/IncomingHeaderBuilder.java b/components/net/sf/briar/protocol/IncomingHeaderBuilder.java new file mode 100644 index 0000000000..04371f8497 --- /dev/null +++ b/components/net/sf/briar/protocol/IncomingHeaderBuilder.java @@ -0,0 +1,47 @@ +package net.sf.briar.protocol; + +import java.io.IOException; +import java.security.InvalidKeyException; +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.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, SignatureException, + InvalidKeyException { + if(sig == null) throw new IllegalStateException(); + byte[] raw = getSignableRepresentation(); + signature.initVerify(keyPair.getPublic()); + signature.update(raw); + signature.verify(sig); + messageDigest.reset(); + messageDigest.update(raw); + messageDigest.update(sig); + byte[] hash = messageDigest.digest(); + Set<BatchId> ackSet = new HashSet<BatchId>(acks); + Set<GroupId> subSet = new HashSet<GroupId>(subs); + return new HeaderImpl(new BundleId(hash), raw.length, ackSet, subSet, + transports, sig); + } +} diff --git a/components/net/sf/briar/protocol/MessageImpl.java b/components/net/sf/briar/protocol/MessageImpl.java index 64306e4e44..3fcbc273b9 100644 --- a/components/net/sf/briar/protocol/MessageImpl.java +++ b/components/net/sf/briar/protocol/MessageImpl.java @@ -48,7 +48,7 @@ public class MessageImpl implements Message { return body.length; } - public byte[] getBody() { + public byte[] getBytes() { return body; } diff --git a/components/net/sf/briar/protocol/OutgoingBatchBuilder.java b/components/net/sf/briar/protocol/OutgoingBatchBuilder.java new file mode 100644 index 0000000000..1f06d42539 --- /dev/null +++ b/components/net/sf/briar/protocol/OutgoingBatchBuilder.java @@ -0,0 +1,37 @@ +package net.sf.briar.protocol; + +import java.io.IOException; +import java.security.InvalidKeyException; +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.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, SignatureException, + InvalidKeyException { + byte[] raw = getSignableRepresentation(); + signature.initSign(keyPair.getPrivate()); + signature.update(raw); + byte[] sig = signature.sign(); + messageDigest.reset(); + messageDigest.update(raw); + messageDigest.update(sig); + byte[] hash = messageDigest.digest(); + return new BatchImpl(new BatchId(hash), raw.length, messages, sig); + } +} diff --git a/components/net/sf/briar/protocol/OutgoingHeaderBuilder.java b/components/net/sf/briar/protocol/OutgoingHeaderBuilder.java new file mode 100644 index 0000000000..268ec5cc69 --- /dev/null +++ b/components/net/sf/briar/protocol/OutgoingHeaderBuilder.java @@ -0,0 +1,44 @@ +package net.sf.briar.protocol; + +import java.io.IOException; +import java.security.InvalidKeyException; +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.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, SignatureException, + InvalidKeyException { + byte[] raw = getSignableRepresentation(); + signature.initSign(keyPair.getPrivate()); + signature.update(raw); + byte[] sig = signature.sign(); + messageDigest.reset(); + messageDigest.update(raw); + messageDigest.update(sig); + byte[] hash = messageDigest.digest(); + Set<BatchId> ackSet = new HashSet<BatchId>(acks); + Set<GroupId> subSet = new HashSet<GroupId>(subs); + return new HeaderImpl(new BundleId(hash), raw.length, ackSet, subSet, + transports, sig); + } +} diff --git a/components/net/sf/briar/serial/ReaderFactoryImpl.java b/components/net/sf/briar/serial/ReaderFactoryImpl.java index dd2a1a4da6..2f52c03e2e 100644 --- a/components/net/sf/briar/serial/ReaderFactoryImpl.java +++ b/components/net/sf/briar/serial/ReaderFactoryImpl.java @@ -5,7 +5,7 @@ import java.io.InputStream; import net.sf.briar.api.serial.Reader; import net.sf.briar.api.serial.ReaderFactory; -class ReaderFactoryImpl implements ReaderFactory { +public class ReaderFactoryImpl implements ReaderFactory { public Reader createReader(InputStream in) { return new ReaderImpl(in); diff --git a/components/net/sf/briar/serial/ReaderImpl.java b/components/net/sf/briar/serial/ReaderImpl.java index a704334158..b98383e0e6 100644 --- a/components/net/sf/briar/serial/ReaderImpl.java +++ b/components/net/sf/briar/serial/ReaderImpl.java @@ -53,6 +53,10 @@ class ReaderImpl implements Reader { readLimit = 0L; } + public void close() throws IOException { + in.close(); + } + public boolean hasBoolean() throws IOException { if(!started) readNext(true); if(eof) return false; diff --git a/components/net/sf/briar/serial/WriterFactoryImpl.java b/components/net/sf/briar/serial/WriterFactoryImpl.java index 63563fdf34..de01ca18f4 100644 --- a/components/net/sf/briar/serial/WriterFactoryImpl.java +++ b/components/net/sf/briar/serial/WriterFactoryImpl.java @@ -5,7 +5,7 @@ import java.io.OutputStream; import net.sf.briar.api.serial.Writer; import net.sf.briar.api.serial.WriterFactory; -class WriterFactoryImpl implements WriterFactory { +public class WriterFactoryImpl implements WriterFactory { public Writer createWriter(OutputStream out) { return new WriterImpl(out); diff --git a/components/net/sf/briar/serial/WriterImpl.java b/components/net/sf/briar/serial/WriterImpl.java index eb20d6b029..18f1760780 100644 --- a/components/net/sf/briar/serial/WriterImpl.java +++ b/components/net/sf/briar/serial/WriterImpl.java @@ -18,6 +18,11 @@ class WriterImpl implements Writer { this.out = out; } + public void close() throws IOException { + out.flush(); + out.close(); + } + public void writeBoolean(boolean b) throws IOException { if(b) out.write(Tag.TRUE); else out.write(Tag.FALSE); @@ -156,9 +161,4 @@ class WriterImpl implements Writer { public void writeNull() throws IOException { out.write(Tag.NULL); } - - public void close() throws IOException { - out.flush(); - out.close(); - } } diff --git a/test/build.xml b/test/build.xml index f6a2653a30..00bd9fb3ef 100644 --- a/test/build.xml +++ b/test/build.xml @@ -21,6 +21,7 @@ <test name='net.sf.briar.i18n.I18nTest'/> <test name='net.sf.briar.invitation.InvitationWorkerTest'/> <test name='net.sf.briar.protocol.BundleReaderTest'/> + <test name='net.sf.briar.protocol.BundleReadWriteTest'/> <test name='net.sf.briar.protocol.BundleWriterTest'/> <test name='net.sf.briar.serial.ReaderImplTest'/> <test name='net.sf.briar.serial.WriterImplTest'/> diff --git a/test/net/sf/briar/db/DatabaseComponentTest.java b/test/net/sf/briar/db/DatabaseComponentTest.java index 174c3a6afd..fc8fa86343 100644 --- a/test/net/sf/briar/db/DatabaseComponentTest.java +++ b/test/net/sf/briar/db/DatabaseComponentTest.java @@ -1,7 +1,5 @@ package net.sf.briar.db; -import java.io.IOException; -import java.security.SignatureException; import java.util.Collections; import java.util.Map; import java.util.Set; @@ -18,9 +16,9 @@ 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.Bundle; -import net.sf.briar.api.protocol.BundleBuilder; 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; @@ -507,7 +505,7 @@ public abstract class DatabaseComponentTest extends TestCase { @Test public void testGenerateBundleThrowsExceptionIfContactIsMissing() - throws DbException, IOException, SignatureException { + throws Exception { Mockery context = new Mockery(); @SuppressWarnings("unchecked") final Database<Object> database = context.mock(Database.class); @@ -518,7 +516,7 @@ public abstract class DatabaseComponentTest extends TestCase { @SuppressWarnings("unchecked") final Provider<BatchBuilder> batchBuilderProvider = context.mock(Provider.class, "batchBuilderProvider"); - final BundleBuilder bundleBuilder = context.mock(BundleBuilder.class); + final BundleWriter bundleBuilder = context.mock(BundleWriter.class); context.checking(new Expectations() {{ // Check that the contact is still in the DB oneOf(database).startTransaction(); @@ -539,8 +537,7 @@ public abstract class DatabaseComponentTest extends TestCase { } @Test - public void testGenerateBundle() throws DbException, IOException, - SignatureException { + public void testGenerateBundle() throws Exception { final long headerSize = 1234L; Mockery context = new Mockery(); @SuppressWarnings("unchecked") @@ -552,12 +549,11 @@ public abstract class DatabaseComponentTest extends TestCase { @SuppressWarnings("unchecked") final Provider<BatchBuilder> batchBuilderProvider = context.mock(Provider.class, "batchBuilderProvider"); - final BundleBuilder bundleBuilder = context.mock(BundleBuilder.class); + final BundleWriter bundleWriter = context.mock(BundleWriter.class); final HeaderBuilder headerBuilder = context.mock(HeaderBuilder.class); final Header header = context.mock(Header.class); final BatchBuilder batchBuilder = context.mock(BatchBuilder.class); final Batch batch = context.mock(Batch.class); - final Bundle bundle = context.mock(Bundle.class); context.checking(new Expectations() {{ allowing(database).startTransaction(); will(returnValue(txn)); @@ -582,11 +578,11 @@ public abstract class DatabaseComponentTest extends TestCase { // Build the header oneOf(headerBuilder).build(); will(returnValue(header)); - oneOf(bundleBuilder).getCapacity(); + oneOf(bundleWriter).getCapacity(); will(returnValue(1024L * 1024L)); oneOf(header).getSize(); will(returnValue(headerSize)); - oneOf(bundleBuilder).addHeader(header); + oneOf(bundleWriter).addHeader(header); // Add a batch to the bundle oneOf(database).getSendableMessages(txn, contactId, Batch.MAX_SIZE - headerSize); @@ -604,25 +600,24 @@ public abstract class DatabaseComponentTest extends TestCase { oneOf(database).addOutstandingBatch( txn, contactId, batchId, messages); // Add the batch to the bundle - oneOf(bundleBuilder).addBatch(batch); + oneOf(bundleWriter).addBatch(batch); // Check whether to add another batch oneOf(batch).getSize(); will(returnValue((long) message.getSize())); // No, just send the bundle - oneOf(bundleBuilder).build(); - will(returnValue(bundle)); + oneOf(bundleWriter).close(); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, headerBuilderProvider, batchBuilderProvider); - db.generateBundle(contactId, bundleBuilder); + db.generateBundle(contactId, bundleWriter); context.assertIsSatisfied(); } @Test public void testReceiveBundleThrowsExceptionIfContactIsMissing() - throws DbException, IOException, SignatureException { + throws Exception { Mockery context = new Mockery(); @SuppressWarnings("unchecked") final Database<Object> database = context.mock(Database.class); @@ -633,7 +628,7 @@ public abstract class DatabaseComponentTest extends TestCase { @SuppressWarnings("unchecked") final Provider<BatchBuilder> batchBuilderProvider = context.mock(Provider.class, "batchBuilderProvider"); - final Bundle bundle = context.mock(Bundle.class); + final BundleReader bundleReader = context.mock(BundleReader.class); context.checking(new Expectations() {{ // Check that the contact is still in the DB oneOf(database).startTransaction(); @@ -646,7 +641,7 @@ public abstract class DatabaseComponentTest extends TestCase { headerBuilderProvider, batchBuilderProvider); try { - db.receiveBundle(contactId, bundle); + db.receiveBundle(contactId, bundleReader); assertTrue(false); } catch(NoSuchContactException expected) {} @@ -654,8 +649,7 @@ public abstract class DatabaseComponentTest extends TestCase { } @Test - public void testReceivedBundle() throws DbException, IOException, - SignatureException { + public void testReceiveBundle() throws Exception { Mockery context = new Mockery(); @SuppressWarnings("unchecked") final Database<Object> database = context.mock(Database.class); @@ -666,7 +660,7 @@ public abstract class DatabaseComponentTest extends TestCase { @SuppressWarnings("unchecked") final Provider<BatchBuilder> batchBuilderProvider = context.mock(Provider.class, "batchBuilderProvider"); - final Bundle bundle = context.mock(Bundle.class); + final BundleReader bundleReader = context.mock(BundleReader.class); final Header header = context.mock(Header.class); final Batch batch = context.mock(Batch.class); context.checking(new Expectations() {{ @@ -676,7 +670,7 @@ public abstract class DatabaseComponentTest extends TestCase { allowing(database).containsContact(txn, contactId); will(returnValue(true)); // Header - oneOf(bundle).getHeader(); + oneOf(bundleReader).getHeader(); will(returnValue(header)); // Acks oneOf(header).getAcks(); @@ -692,7 +686,7 @@ public abstract class DatabaseComponentTest extends TestCase { will(returnValue(transports)); oneOf(database).setTransports(txn, contactId, transports); // Batches - oneOf(bundle).getNextBatch(); + oneOf(bundleReader).getNextBatch(); will(returnValue(batch)); oneOf(batch).getMessages(); will(returnValue(Collections.singleton(message))); @@ -706,8 +700,9 @@ public abstract class DatabaseComponentTest extends TestCase { will(returnValue(batchId)); oneOf(database).addBatchToAck(txn, contactId, batchId); // Any more batches? Nope - oneOf(bundle).getNextBatch(); + oneOf(bundleReader).getNextBatch(); will(returnValue(null)); + oneOf(bundleReader).close(); // Lost batches oneOf(header).getId(); will(returnValue(bundleId)); @@ -718,7 +713,7 @@ public abstract class DatabaseComponentTest extends TestCase { DatabaseComponent db = createDatabaseComponent(database, cleaner, headerBuilderProvider, batchBuilderProvider); - db.receiveBundle(contactId, bundle); + db.receiveBundle(contactId, bundleReader); context.assertIsSatisfied(); } diff --git a/test/net/sf/briar/db/H2DatabaseTest.java b/test/net/sf/briar/db/H2DatabaseTest.java index f89dbc974f..9596a1930d 100644 --- a/test/net/sf/briar/db/H2DatabaseTest.java +++ b/test/net/sf/briar/db/H2DatabaseTest.java @@ -110,7 +110,7 @@ public class H2DatabaseTest extends TestCase { assertEquals(authorId, m1.getAuthor()); assertEquals(timestamp, m1.getTimestamp()); assertEquals(size, m1.getSize()); - assertTrue(Arrays.equals(body, m1.getBody())); + assertTrue(Arrays.equals(body, m1.getBytes())); // Delete the records db.removeContact(txn, contactId); db.removeMessage(txn, messageId); diff --git a/test/net/sf/briar/protocol/BundleReadWriteTest.java b/test/net/sf/briar/protocol/BundleReadWriteTest.java new file mode 100644 index 0000000000..52b4824133 --- /dev/null +++ b/test/net/sf/briar/protocol/BundleReadWriteTest.java @@ -0,0 +1,156 @@ +package net.sf.briar.protocol; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.Signature; +import java.security.SignatureException; +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.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.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.api.protocol.MessageParser; +import net.sf.briar.api.protocol.UniqueId; +import net.sf.briar.api.serial.FormatException; +import net.sf.briar.api.serial.Reader; +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; + +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"; + private static final String KEY_PAIR_ALGO = "RSA"; + private static final String DIGEST_ALGO = "SHA-256"; + + private final File testDir = TestUtils.getTestDirectory(); + private final File bundle = new File(testDir, "bundle"); + + private final long capacity = 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 MessageId messageId = new MessageId(TestUtils.getRandomId()); + private final AuthorId authorId = new AuthorId(TestUtils.getRandomId()); + private final long timestamp = System.currentTimeMillis(); + private final byte[] messageBody = new byte[123]; + private final Message message = new MessageImpl(messageId, MessageId.NONE, + sub, authorId, timestamp, messageBody); + + // FIXME: This test should not depend on an impl in another component + private final WriterFactory wf = new WriterFactoryImpl(); + + private final KeyPair keyPair; + private final Signature sig; + private final MessageDigest digest; + + public BundleReadWriteTest() throws NoSuchAlgorithmException { + super(); + keyPair = KeyPairGenerator.getInstance(KEY_PAIR_ALGO).generateKeyPair(); + sig = Signature.getInstance(SIGNATURE_ALGO); + digest = MessageDigest.getInstance(DIGEST_ALGO); + assertEquals(digest.getDigestLength(), UniqueId.LENGTH); + } + + @Before + public void setUp() { + testDir.mkdirs(); + } + + @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); + + w.addHeader(header); + w.addBatch(batch); + w.close(); + + assertTrue(bundle.exists()); + assertTrue(bundle.length() > messageBody.length); + } + + @Test + public void testWriteAndReadBundle() throws Exception { + + testWriteBundle(); + + MessageParser messageParser = new MessageParser() { + public Message parseMessage(byte[] body) throws FormatException, + SignatureException { + // FIXME: Really parse the message + return message; + } + }; + Provider<HeaderBuilder> headerBuilderProvider = + new Provider<HeaderBuilder>() { + public HeaderBuilder get() { + return new IncomingHeaderBuilder(keyPair, sig, digest, wf); + } + }; + Provider<BatchBuilder> batchBuilderProvider = + new Provider<BatchBuilder>() { + public BatchBuilder get() { + return new IncomingBatchBuilder(keyPair, sig, digest, wf); + } + }; + + FileInputStream in = new FileInputStream(bundle); + Reader reader = new ReaderFactoryImpl().createReader(in); + BundleReader r = new BundleReaderImpl(reader, bundle.length(), + messageParser, headerBuilderProvider, batchBuilderProvider); + + Header h = r.getHeader(); + assertEquals(acks, h.getAcks()); + assertEquals(subs, h.getSubscriptions()); + assertEquals(transports, h.getTransports()); + Batch b = r.getNextBatch(); + assertEquals(Collections.singletonList(message), b.getMessages()); + assertNull(r.getNextBatch()); + r.close(); + } + + @After + public void tearDown() { + TestUtils.deleteTestDirectory(testDir); + } +} diff --git a/test/net/sf/briar/protocol/BundleReaderTest.java b/test/net/sf/briar/protocol/BundleReaderTest.java index b0a5f5d012..1923416f4b 100644 --- a/test/net/sf/briar/protocol/BundleReaderTest.java +++ b/test/net/sf/briar/protocol/BundleReaderTest.java @@ -12,6 +12,7 @@ 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; @@ -46,7 +47,7 @@ public class BundleReaderTest extends TestCase { private final byte[] batchSig = TestUtils.getRandomId(); @Test - public void testGetHeader() throws IOException, SignatureException { + public void testGetHeader() throws Exception { Mockery context = new Mockery(); final Reader reader = context.mock(Reader.class); final MessageParser messageParser = context.mock(MessageParser.class); @@ -82,7 +83,7 @@ public class BundleReaderTest extends TestCase { oneOf(headerBuilder).build(); will(returnValue(header)); }}); - BundleReader r = createBundleReader(reader, messageParser, + BundleReader r = new BundleReaderImpl(reader, size, messageParser, headerBuilderProvider, batchBuilderProvider); assertEquals(header, r.getHeader()); @@ -91,8 +92,7 @@ public class BundleReaderTest extends TestCase { } @Test - public void testBatchBeforeHeaderThrowsException() throws IOException, - SignatureException { + public void testBatchBeforeHeaderThrowsException() throws Exception { Mockery context = new Mockery(); final Reader reader = context.mock(Reader.class); final MessageParser messageParser = context.mock(MessageParser.class); @@ -102,7 +102,7 @@ public class BundleReaderTest extends TestCase { @SuppressWarnings("unchecked") final Provider<BatchBuilder> batchBuilderProvider = context.mock(Provider.class, "batchBuilderProvider"); - BundleReader r = createBundleReader(reader, messageParser, + BundleReader r = new BundleReaderImpl(reader, size, messageParser, headerBuilderProvider, batchBuilderProvider); try { @@ -114,8 +114,30 @@ public class BundleReaderTest extends TestCase { } @Test - public void testGetHeaderNoBatches() throws IOException, + 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); @@ -155,19 +177,21 @@ public class BundleReaderTest extends TestCase { oneOf(reader).hasListEnd(); will(returnValue(true)); oneOf(reader).readListEnd(); + // Close + oneOf(reader).close(); }}); - BundleReader r = createBundleReader(reader, messageParser, + 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 IOException, - SignatureException { + public void testGetHeaderOneBatch() throws Exception { Mockery context = new Mockery(); final Reader reader = context.mock(Reader.class); final MessageParser messageParser = context.mock(MessageParser.class); @@ -226,26 +250,17 @@ public class BundleReaderTest extends TestCase { oneOf(reader).hasListEnd(); will(returnValue(true)); oneOf(reader).readListEnd(); + // Close + oneOf(reader).close(); }}); - BundleReader r = createBundleReader(reader, messageParser, + BundleReader r = new BundleReaderImpl(reader, size, messageParser, headerBuilderProvider, batchBuilderProvider); assertEquals(header, r.getHeader()); assertEquals(batch, r.getNextBatch()); assertNull(r.getNextBatch()); + r.close(); context.assertIsSatisfied(); } - - private BundleReader createBundleReader(Reader reader, - MessageParser messageParser, - Provider<HeaderBuilder> headerBuilderProvider, - Provider<BatchBuilder> batchBuilderProvider) { - return new BundleReader(reader, messageParser, headerBuilderProvider, - batchBuilderProvider) { - public long getSize() { - return size; - } - }; - } } diff --git a/test/net/sf/briar/protocol/BundleWriterTest.java b/test/net/sf/briar/protocol/BundleWriterTest.java index a2e0253003..2ba6fedd38 100644 --- a/test/net/sf/briar/protocol/BundleWriterTest.java +++ b/test/net/sf/briar/protocol/BundleWriterTest.java @@ -9,7 +9,7 @@ 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.Bundle; +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; @@ -21,6 +21,7 @@ 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()); @@ -28,7 +29,6 @@ public class BundleWriterTest extends TestCase { private final Map<String, String> transports = Collections.singletonMap("foo", "bar"); private final byte[] headerSig = TestUtils.getRandomId(); - private final long capacity = 1024L * 1024L; private final byte[] messageBody = new byte[123]; private final byte[] batchSig = TestUtils.getRandomId(); @@ -59,7 +59,7 @@ public class BundleWriterTest extends TestCase { will(returnValue(headerSig)); oneOf(writer).writeRaw(headerSig); }}); - BundleWriter w = createBundleWriter(writer); + BundleWriter w = new BundleWriterImpl(writer, capacity); w.addHeader(header); @@ -91,7 +91,7 @@ public class BundleWriterTest extends TestCase { will(returnValue(headerSig)); oneOf(writer).writeRaw(headerSig); }}); - BundleWriter w = createBundleWriter(writer); + BundleWriter w = new BundleWriterImpl(writer, capacity); w.addHeader(header); @@ -103,7 +103,7 @@ public class BundleWriterTest extends TestCase { Mockery context = new Mockery(); final Writer writer = context.mock(Writer.class); final Batch batch = context.mock(Batch.class); - BundleWriter w = createBundleWriter(writer); + BundleWriter w = new BundleWriterImpl(writer, capacity); try { w.addBatch(batch); @@ -117,7 +117,7 @@ public class BundleWriterTest extends TestCase { public void testCloseBeforeHeaderThrowsException() throws IOException { Mockery context = new Mockery(); final Writer writer = context.mock(Writer.class); - BundleWriter w = createBundleWriter(writer); + BundleWriter w = new BundleWriterImpl(writer, capacity); try { w.close(); @@ -159,7 +159,7 @@ public class BundleWriterTest extends TestCase { oneOf(writer).writeListEnd(); oneOf(writer).close(); }}); - BundleWriter w = createBundleWriter(writer); + BundleWriter w = new BundleWriterImpl(writer, capacity); w.addHeader(header); w.close(); @@ -200,7 +200,7 @@ public class BundleWriterTest extends TestCase { oneOf(writer).writeListStart(); oneOf(batch).getMessages(); will(returnValue(Collections.singleton(message))); - oneOf(message).getBody(); + oneOf(message).getBytes(); will(returnValue(messageBody)); oneOf(writer).writeRaw(messageBody); oneOf(writer).writeListEnd(); @@ -211,7 +211,7 @@ public class BundleWriterTest extends TestCase { oneOf(writer).writeListStart(); oneOf(batch).getMessages(); will(returnValue(Collections.singleton(message))); - oneOf(message).getBody(); + oneOf(message).getBytes(); will(returnValue(messageBody)); oneOf(writer).writeRaw(messageBody); oneOf(writer).writeListEnd(); @@ -222,7 +222,7 @@ public class BundleWriterTest extends TestCase { oneOf(writer).writeListEnd(); oneOf(writer).close(); }}); - BundleWriter w = createBundleWriter(writer); + BundleWriter w = new BundleWriterImpl(writer, capacity); w.addHeader(header); w.addBatch(batch); @@ -231,12 +231,4 @@ public class BundleWriterTest extends TestCase { context.assertIsSatisfied(); } - - private BundleWriter createBundleWriter(Writer writer) { - return new BundleWriter(writer, capacity) { - public Bundle build() throws IOException { - return null; - } - }; - } } -- GitLab